Python 爬虫框架 Scrapy 快速使用

Scrapy 快速使用

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中;

Scrapy 官方文档:https://doc.scrapy.org/en/latest/ 不完全中文文档:
http://scrapy-chs.readthedocs.io/zh_CN/1.0/

以下以抓取 CSDN 用户博客数据为例介绍 Scrapy 的快速使用,爬取思路如下:
爬取 CSDN 的用户博客的信息,可在 
http://blog.csdn.net
/user_id
 页面上获取用户博客信息,通过 http://my.csdn.net/user_id 上用户的关注,被关注列表获取其他的用户;


下载安装 Scrapy

可以直接使用 pip 工具下载安装:

pip installl scrapy

下载安装出现异常参见
https://www.cnblogs.com/liuliliuli2017/p/6746440.html

初始化项目

这里直接使用官方提供的工具初始化项目,在工作目录下:

scrapy startproject csdn

创建的目录如下:

csdn/
    scrapy.cfg           # 项目配置文件
    csdn/                # 该项目的 python 模块
        __init__.py
        items.py         # 项目中 item 实现类的文件
        pipelines.py     # 项目中 pipeline 实现类的文件
        settings.py      # 项目的设置文件
        spiders/         # 存放 spider 代码的模块
            __init__.py
            ....


定义 Item 类存储数据

Item 是 scrapy 储存爬取数据的容器,并提供了额外的保护机制,以下在 pipelines.py 中实现一个保存用户博客数据的 Item:

# @File: pipelines.py 
import scrapy

# 博客数据对象
class BlogItem(scrapy.Item):
    user_id = scrapy.Field()                   # 用户Id
    user_name = scrapy.Field()                 # 用户名
    visit_count = scrapy.Field()               # 博客访问数
    rank = scrapy.Field()                      # 博客排名


编写 Spider 对象

Spider 是用用于从单个或多个网站爬取数据的类,为主要的爬取逻辑; 创建 Soider 只需要在 spiders 目录下创建一个继承 scrapy.Spider 的类,并覆盖 scrapy() 方法即可,当然也可以通过 scrapy 提供的工具自动创建,进入 csdn 目录,以如下命令:

scrapy genspider <spider_name> <allowed_domains>

创建一个用于爬取博客的爬虫对象 blogSpider 之后填充 blog.py 的代码

# @File: spiders/blog.py 
import scrapy
from scrapy import log
from csdn.items import BlogItem


class BlogSpider(scrapy.Spider):

    # 任务名称
    name = 'blog'
    # 允许访问域名
    allowed_domains = ['my.csdn.net', 'blog.csdn.net']
    # 起始 URL
    start_urls = ['http://my.csdn.net/al_assad']

    # 访问用户主页,获取用户列表
    def parse(self, response):
        blog_page_url = 'http://blog.csdn.net/' + response.url.split('/')[-1]
        scrapy.http.Request(blog_page_url, self.blog_parse)               # 发送blog页面的请求,响应对象的回调方法会 blog_parse
        user_list = response.xpath('//div[@class="header clearfix"]/a/@href').extract()

        for user_name in user_list:
            user_page_url = 'http://my.csdn.net/'+user_name
            yield scrapy.http.Request(user_page_url, self.parse)          # 发送用户页面请求,响应对象的回调方法为 parse


    # 访问用户博客页面,获取博客数据(分为2种页面风格获取信息)
    def blog_parse(self, response):
        blog_item = BlogItem()

        if response.xpath('//ul[@class="panel_body profile"]'):  # 旧版博客页面
            blog_item['user_id'] = response.url.split('/')[-1]
            blog_item['user_name'] = response.xpath('//a[@class="user_name"]/text()').extract()[0]
            blog_item['visit_count'] = response.xpath('//ul[@id="blog_rank"]/li[1]/span/text()').extract()[0][:-1]
            blog_item['rank'] = response.xpath('//ul[@id="blog_rank"]/li[1]/span/text()').extract()[0][1:-1]
        else:                                                     # 新版博客页面
            blog_item['user_id'] = response.url.split('/')[-1]
            blog_item['user_name'] = response.xpath('//a[@id="uid"]/text()').extract()[0]
            blog_item['visit_count'] = ''.join(response.xpath('//div[@class="gradeAndbadge"][1]/span[@class="num"]/text()').extract()[0].split(','))
            blog_item['rank'] = response.xpath('//div[@class="gradeAndbadge"][3]/span[@class="num"]/text()').extract()[0]

        log.msg(blog_item,level=log.INFO)
        yield blog_item


运行爬虫任务

在 scrapy 爬虫项目的路径下,可以通过类似如下启动项目的某一个 spider:

scrapy crawl blog         # 启动 name = ‘blog’ 的 Spider 任务 

如果不想输出日志,可以直接通过以下:

scrapy crawl blog --nolog

如果想将 Item 数据直接保存为 json 文件,scrapy 提供了默认的实现,可以如下启动 spider:

scrapy crawl blog -o data.json

实现 Pipeline 

scrapy 数据传输的流程如下,由 Spider 从 Reponse 获取数据,进行处理后,结构化储存在 Item 中,再交给不同优先级别 Pipeline 对象对 Item 进行进一步处理,这个过程可以是异步进行的; 基于这个特性,Pipeline 特别适合用来进行数据项去重,格式化输出文件,数据库持久化;

以下在 pipelines.py 文件中实现了 4 个 Pipeline 对象,Pipeline 对象只要包含 process_item 、open_spider、close_spider 等方法即可;

# @File: pipelines.py
import json
import pymysql
from scrapy import log


# 去重过滤器
class DuplicatesPipeline(object):
    def __init__(self):
        self.id_set = set()   #内部使用一个 set() 维护数据

    def process_item(self, item, spider):
        if item['user_id'] not in self.id_set:
            self.id_set.add(item['user_id'])
            return item


# 格式化输出 Json (将每一个 item 都输出为一个 json 字符串)
class JsonWritePipeline(object):
    def __init__(self):
        self.output = open('json_format.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.output.write(line)
        return item

    def close_spider(self):
        self.output.close()


# 格式化输出为CSV文件
class CSVWritePipeline(object):
    def __init__(self):
        self.output = open('data.csv', 'w', encoding='utf-8')
        self.output.write('user_id,user_name,visit_count,rank'+"\n")

    def process_item(self, item, spider):
        line = self.__checkout(item['user_id']) + ',' + self.__checkout(item['user_name']) + ',' \
            + self.__checkout(item['visit_count']) + ',' + self.__checkout(item['rank'])
        self.output.write(line)
        return item

    def __checkout(self, src_str):
        for ch in str(src_str):
            if ch == ' ' or ch == ',':
                return '"' + src_str + '"'
        return src_str

    def close_spider(self):
        self.output.close()


# 储存到 MySQL 数据库( 使用 pymysql 模块):不考虑优化,直接实现功能
class MySQLPipeline(object):

    # 启动 spider 时,创建数据库连接对象
    def open_spider(self):
        host = "localhost"
        user = "root"
        password = "23333"
        db_name = "CSDNDB"
        try:
            self.db = pymysql.connect(host,user,password,db_name)
        except:
            log.msg("mysql connection error", level=log.ERROR)


    def process_item(self, item, spider):
        cursor = self.db.cursor()
        sql = "INSERT INTO CSDNDB(user_id,user_name,visit_count,rank) VALUES(%s,%s,%d,%d) " % \
              (item['user_id'], item['user_name'], item['visit_count'], item['rank'])
        try:
            cursor.execute(sql)
            self.db.commit()
        except:
            self.db.rollback()
        return item

    # 关闭 spider 时,销毁数据库连接对象
    def close_spider(self):
        self.db.close()

要对 Item 启用这些 pipeline ,只需要在 settings.py 文件中启用 
ITEM_PIPELINES 常量即可,如下:

# 启用 Item 的 pipeline 对象组
ITEM_PIPELINES = {
    'csdn.pipelines.DuplicatesPipeline': 900,
    # 'csdb.pipelines.JsonWritePipeline': 800,
     'csdb.pipelines.CSVWritePipeline': 500,
    # 'csdb.pipelines.MySQLPipeline': 400,
}

900,800 等参数代表该 pipeline 处理 item 的优先级,数字越高优先级越大,取值 0-999;

以上完整代码地址:
https://github.com/Al-assad/CSDN-blog-crawler

    原文作者:Al_assad
    原文地址: https://blog.csdn.net/Al_assad/article/details/79178052
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞