Scrapy框架应用实践

文章目录

  • 安装
  • 创建项目
  • 文件说明
  • 创建一个spider(爬虫程序)
    • 生成文件xiaoshuo.py
      • Post方式带参数请求网页
  • 定义item
  • 再次打开spider来提取我们想要的信息
  • setting
  • 运行与保存
  • pipline自定义存储格式
  • Dowmloader Middleware的使用
  • 数据流
    • 解释
  • 常见问题
    • DNS lookup failed:no results for hostname lookup
    • 请求网页时传递参数
    • 文件下载&存储

参考原文:
https://www.jianshu.com/p/6bc5a4641629
https://scrapy-chs.readthedocs.io/zh_CN/latest/intro/overview.html

安装

  • pip install scrapy

创建项目

  • 创建一个名为tutorial文件夹
    cmd: scrapy startproject tutorial

文件说明

  • scrapy.cfg:配置文件
  • spiders:存放Spider文件(爬取的py文件)
  • items.py:保存爬取数据的容器,和字典较像
  • middlewares.py:定义Downloader Middlewares(下载器中间件)和Spider Middlewares(蜘蛛中间件)的实现
  • pipelines.py:定义Item Pipeline的实现,实现数据的清洗,储存,验证。
  • settings.py:全局配置(代理设置等)

创建一个spider(爬虫程序)

  • 包含了一个用于下载的初始URL,如何跟进网页中的链接以及如何分析页面中的内容, 提取生成 item 的方法。

  • cmd:scrapy genspider xiaoshuo(py名) https://www.xiaoshuodaquan.com/quanben/(URL)

生成文件xiaoshuo.py

  • name:项目名称,用于区别Spider
  • allowed_domains:是允许爬取的域名,比如一些网站有相关链接,域名就和本网站不同,这些就会忽略。
  • start_urls:Spider爬取的网站,定义初始的请求url,可以多个。
  • parse方法:是Spider的一个方法,在请求start_url后,这个方法对网页解析,与提取自己想要的东西。
  • response参数:是请求网页后返回的内容,也就是你需要解析的网页。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。
# xiaoshuo.py
import scrapy

class XiaoshuoSpider(scrapy.Spider):
    name = 'xiaoshuo'
    allowed_domains = ['https://www.xiaoshuodaquan.com/quanben/']
    start_urls = ['http://https://www.xiaoshuodaquan.com/quanben//']

    def parse(self, response):
        pass

Post方式带参数请求网页

从Network header中构建

# 构造请求头, 对setting中的文件内容进行覆盖
custom_settings = {
    "DEFAULT_REQUEST_HEADERS": {
        'Host': '',
        'Connection': 'keep-alive',
        'Accept': '*/*',
        'Origin': '',
        'X-Requested-With': 'XMLHttpRequest',
        'User-Agent': '',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Referer': '',
        'Accept-Encoding': '',
        'Accept-Language': '',
        'Cookie': ''
    }
}
def form_data_func(self, pageNum):
    # 数据
    formData = {
        'pageNum': str(pageNum),
        'pageSize': str(30),
        'column': 'szse',
        'tabName': 'fulltext',
        'plate': 'sz;sh',
        'category': 'category_ndbg_szsh',
        'seDate': '2017-04-02~2020-04-02',
        'isHLtitle': 'true'
    }
    return formData
 
# 重写start_requests
def start_requests(self):
    # 请求的地址
    url = "http://www.cninfo.com.cn/new/hisAnnouncement/query/"

    # 模拟表单或Ajax提交post请求
    yield scrapy.FormRequest(url, formdata=self.form_data_func(1), callback=self.pageNum_parse)
  • Scrapy为Spider的 start_urls 属性中的每个URL创建了 scrapy.Request 对象,并将 parse 方法作为回调函数(callback)赋值给了Request。
  • Request对象经过调度,执行生成 scrapy.http.Response 对象并送回给spider parse() 方法。

定义item

  • item是保存爬取数据的容器,使用的方法和字典差不多。
# item.py
# 定义想要提取的信息
class TutoriaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    hreflink = scrapy.Field()  # 链接
    title = scrapy.Field()  # 书名
    word_cnt = scrapy.Field()  # 总字数

再次打开spider来提取我们想要的信息

# spider: xiaoshuo.py
item = TutoriaItem()
yield item

setting

  • 代理设置
# Setting.py
USER_AGENT = 'xiaoshuo (+代理IP)'
  • Obey robots.txt rules
ROBOTSTXT_OBEY = False
  • File下载并存储配置
# pipeline优先级设置
ITEM_PIPELINES = {
    'juchao.pipelines.clearDataPipeline':1,
    # 'scrapy.pipelines.files.FilesPipeline':1,
    'juchao.pipelines.JuchaoPipeline': 2,
#    'juchao.pipelines.JuchaoPipeline': 300,
}
FILES_STORE = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'pdf')

运行与保存

  • 运行:scrapy crawl xiaoshuo
  • 保存
    • scrapy crawl xiaoshuo -o xiaoshuo.csv
    • scrapy crawl xiaoshuo-o xiaoshuo.xml
    • scrapy crawl maoyan -o maoyan.json -s FEED_EXPORT_ENCODING=UTF8

pipline自定义存储格式

class TutoriaPipeline(object):
    def __init__(self):
        store_file = os.path.dirname(__file__) + 'xiaoshuo.csv'
        # print("store_file:", store_file)
        self.file = open(store_file, 'w', newline='')
        self.writer = csv.writer(self.file)

    def process_item(self, item, spider):
        # print(item['book_class'])
        # if item['book_class'] == '':
            # print("****************************")
        if item['book_class'] != "" or item['book_class'] != '':
            self.writer.writerow((item['book_title'], item['href_link'],  item['total_words'], item['brief_introduction']))
        return item
    def close_spider(self,spider):
        self.file.close()

Dowmloader Middleware的使用

  • 比如修改User-Agent,使用代理ip等。

数据流

  • ScrapyEngine打开一个网站,找到处理该网站的Spider,并向该Spider请求第一个(批)要爬取的url(s);
  • ScrapyEngine向调度器请求第一个要爬取的url,并加入到Schedule作为请求以备调度;
  • ScrapyEngine向调度器请求下一个要爬取的url;
  • Schedule返回下一个要爬取的url给ScrapyEngine,ScrapyEngine通过DownloaderMiddlewares将url转发给Downloader;
  • 页面下载完毕,Downloader生成一个页面的Response,通过DownloaderMiddlewares发送给ScrapyEngine;
  • ScrapyEngine从Downloader中接收到Response,通过SpiderMiddlewares发送给Spider处理;
  • Spider处理Response并返回提取到的Item以及新的Request给ScrapyEngine;
  • ScrapyEngine将Spider返回的Item交给ItemPipeline,将Spider返回的Request交给Schedule进行从第二步开始的重复操作,直到调度器中没有待处理的Request,ScrapyEngine关闭。

解释

Spiders:爬虫,定义了爬取的逻辑和网页内容的解析规则,主要负责解析响应并生成结果和新的请求
Engine:引擎,处理整个系统的数据流处理,出发事物,框架的核心。
Scheduler:调度器,接受引擎发过来的请求,并将其加入队列中,在引擎再次请求时将请求提供给引擎
Downloader:下载器,下载网页内容,并将下载内容返回给spider
ItemPipeline:项目管道,负责处理spider从网页中抽取的数据,主要是负责清洗,验证和向数据库中存储数据
Downloader Middlewares:下载中间件,是处于Scrapy的Request和Requesponse之间的处理模块
Spider Middlewares:spider中间件,位于引擎和spider之间的框架,主要处理spider输入的响应和输出的结果及新的请求middlewares.py里实现

常见问题

DNS lookup failed:no results for hostname lookup

修改setting.py文件,将ROBOTSTXT_OBEY = True改为False: 拒绝遵守 Robot协议

请求网页时传递参数

把需要传递的信息赋值给这个叫meta的变量,但meta只接受字典类型的赋值,因此要把待传递的信息改成“字典”的形式,即:
meta={‘key1’:value1,‘key2’:value2}
如果想在下一个函数中取出value1,只需得到上一个函数的meta[‘key1’]即可,因为meta是随着Request产生时传递的,下一个函数得到的Response对象中就会有meta,即response.meta,取value1则是value1=response.meta[‘key1’]

文件下载&存储

# item.py
# FilesPipeline处理需要
# 当FilesPipeline处理时,会检测是否有file_urls字段,如果有的话,会将url传送给scarpy调度器和下载器;
# 下载完成之后,会将结果写入item的另一字段files,files包含了文件现在的本地路径(相对于配置FILE_STORE的路径)、文件校验和checksum、文件的url。
file_urls = scrapy.Field()
file = scrapy.Field()


# pipline.py
class JuchaoPipeline(FilesPipeline):  # 继承自FilesPipeline

    def get_media_requests(self, item, info):
        yield scrapy.Request(item['file_urls'], meta = {'secCode':item['secCode'], 'announcementTitle':item['announcementTitle']})

    # 修改文件名
    def file_path(self, request, response=None, info=None):
        # print(response)
        secCode = request.meta['secCode']
        announcementTitle = request.meta['announcementTitle']
        # print(secCode, announcementTitle)
        # input()
        return '%s_%s.pdf' % (secCode, announcementTitle)

你可能感兴趣的:(Scrapy)