再探Scrapy爬虫框架


写在前面

爬虫框架也是需要掌握的知识,这次尝试抓取药监局的化妆品信息,并保存到excel和MongoDB数据库中。

目标地址


化妆品生产许可信息服务平台


准备工作

#创建scrapy项目
scrapy startproject NMPA
#进入项目目录
cd NMPA
#创建spider
scrapy genspider nmpaspider "scxk.nmpa.gov.cn"

修改items

import scrapy

class NmpaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    epsName = scrapy.Field() #企业名称
    productSn = scrapy.Field() #许可证编号
    certStr = scrapy.Field() #许可项目
    epsAddress = scrapy.Field() #企业住所
    epsProductAddress = scrapy.Field() #生产地址
    businessLicenseNumber = scrapy.Field() #社会信用代码
    legalPerson = scrapy.Field() #法定代表人
    businessPerson = scrapy.Field() #企业负责人
    qualityPerson = scrapy.Field() #质量负责人
    qfManagerName = scrapy.Field() #发证机关
    xkName = scrapy.Field() #签发人
    rcManagerDepartName = scrapy.Field() #日常监督管理机构
    rcManagerUser = scrapy.Field() #日常监督管理人员
    xkDate = scrapy.Field() #有效期至
    xkDateStr = scrapy.Field() #发证日期

抓取模块

修改spiders目录下的nmpaspider.py文件

import scrapy
from NMPA.items import NmpaItem

class NmpaspiderSpider(scrapy.Spider):
    #爬虫名称
    name = 'nmpaspider'
    #网页请求限定在此域名下
    allowed_domains = ['scxk.nmpa.gov.cn']
    #重写start_requests方法,循环抓取多页
    def start_requests(self):
        #向目标地址发起post请求
        url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
        #一共有362页
        for i in range(1,363):
            #page参数决定目标页
            data = {
                'on':'true',
                'page':str(i),
                'pageSize':'15',
                'productNone':'',
                'conditionType':'1',
                'applyname':'',
                'applysn':'',
            }
            #发起post请求,并将response传递给parse方法
            yield scrapy.FormRequest(url = url, callback = self.parse,formdata = data)

    def parse(self, response):
        #获取当前页中的id,用于请求详细信息
        id_lists = response.json()['list']
        #详细信息的url
        url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
        #遍历每一个id
        for id_ in id_lists:
            #构造请求的参数
            data = {'id':id_['ID']}
            #发起post请求,并将response传递给get_details方法
            yield scrapy.FormRequest(url = url,formdata = data,callback = self.get_details)

    def get_details(self,response):
        #创建item对象
        item = NmpaItem()
        #将response转换为json字典
        details = response.json()
        #为item对象赋值
        item['epsName'] = details['epsName']
        item['productSn'] = details['productSn']
        item['certStr'] = details['certStr']
        item['epsAddress'] = details['epsAddress']
        item['epsProductAddress'] = details['epsProductAddress']
        item['businessLicenseNumber'] = details['businessLicenseNumber']
        item['legalPerson'] = details['legalPerson']
        item['businessPerson'] = details['businessPerson']
        item['qualityPerson'] = details['qualityPerson']
        item['qfManagerName'] = details['qfManagerName']
        item['xkName'] = details['xkName']
        item['rcManagerDepartName'] = details['rcManagerDepartName']
        item['rcManagerUser'] = details['rcManagerUser']
        item['xkDate'] = details['xkDate']
        item['xkDateStr'] = details['xkDateStr']
        yield item

管道文件

pipelines.py

#操作MongoDB
import pymongo
#操作Excel
import openpyxl

#数据库索引及Excel表头
title_list = [
            '企业名称', '许可证编号', '许可项目',
            '企业住所', '生产地址', '社会信用代码', '法定代表人',
            '企业负责人', '质量负责人', '发证机关','签发人',
            '日常监督管理机构', '日常监督管理人员', '有效期至', '发证日期'
        ]

#这个类是创建的时候自带的
class NmpaPipeline:
    def process_item(self, item, spider):
        return item

#关于数据库的管道
class mongodbpipeline:
    #初始化
    def __init__(self):
        #连接数据库,请替换user和passwd
        self.client = pymongo.MongoClient('mongodb://user:passwd@localhost:27017')
        #目标数据库,不存在则自动创建
        self.db = self.client.nmpa
        #目标table,不存在则自动创建
        self.collection = self.db.data

    def process_item(self,item,spider):
        #创建一个空字典用于保存重构后的数据
        dict_items = {}
        #将item转换为字典格式
        item = dict(item)
        #重构数据,将字典中的键替换为中文
        for k,v in zip(title_list,item.keys()):
            dict_items[k] = item[v]
        #在数据库中插入一条
        self.collection.insert(dict_items)
        #将item返回,执行下一管道
        return item

    #此方法只在管道执行完毕后运行一次,可以自定义输出信息
    def close_spider(self,scrapy):
        print('MongoDB执行完毕')

#关于Excel的管道
class openpyxlpipeline:
    #初始化
    def __init__(self):
        #创建工作表
        self.wb = openpyxl.Workbook()
        #创建sheet
        self.ws = self.wb.active
        #插入索引
        self.ws.append(title_list)

    def process_item(self,item,spider):
        #创建空列表用于保存一行信息
        list_items = []
        #将item转换为字典格式
        item = dict(item)
        #将字典的值保存到列表
        for v in item.keys():
            list_items.append(item[v])
        #向表格中插入一行数据
        self.ws.append(list_items)
        #返回item以执行下一管道
        return item

    #在管道执行完毕后保存工作表
    def close_spider(self,scrapy):
        self.wb.save('nmpa.xlsx')
        print('Openpyxl执行完毕')

修改配置

settings.py

#关闭robot检查
ROBOTSTXT_OBEY = False
#自定义延迟(发生在两个请求之间)
DOWNLOAD_DELAY = 0.25
#关闭cookie缓存
COOKIES_ENABLED = False
#开启中间件
DOWNLOADER_MIDDLEWARES = {
    'NMPA.middlewares.NmpaDownloaderMiddleware': 543,
}
#管道执行的顺序,数字越小优先级越高,数字为0-1000
ITEM_PIPELINES = {
    'NMPA.pipelines.mongodbpipeline': 300,
    'NMPA.pipelines.openpyxlpipeline': 301,
}

伪造请求头

修改middlewares.py

from fake_useragent import UserAgent
#该方法位于NmpaDownloaderMiddleware类下
def process_request(self, request, spider):
    ua = UserAgent().random
    request.headers['User-Agent'] = ua
    return None

运行爬虫

#普通运行(在NMPA目录下)
scrapy crawl nmpaspider
#禁用日志信息
scrapy crawl nmpaspider -s LOG_ENABLED=0
#将日志信息保存到log文件
scrapy crawl nmpaspider -s LOG_FILE=nmpa.log