Python爬虫:使用多进程,让Scrapy采集速度如虎添翼.


运行环境 Runtime environment

1
2
3
4
操作系统: Windos10  
IDE: pycharm 2021.3.1 x64
语言: python v3.9.1
框架: Scrapy v2.4.1

背景

应用场景,站源的数据是按照日期区间(一年的第一天到该年的最后一天),来进行检索,根据结果整合成url列表来进行采集。

那么,这个列表中的url数量就会相当的庞大。

倘若对它进行切分,用多个相同的Scrapy爬虫,来对切分好的各个分段url进行采集,

加上scrapy自身的并发,那采集速度就可以指数倍增长。

当然,这也会消耗性能,对服务器的配置要求也会提高,代理IP消耗量也会增加。

就算使用批量插入数据,可能也会把mysql日到翻白眼…

Scrapy是线程级别,实现异步。

使用多个进程,来多次跑Scrapy爬虫。

为了避免本文篇幅过长,仅介绍如何多进程跑scrapy,不介绍如何切分url列表,让scrapy区间采集。

multiprocessing 模块介绍

python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。

Python提供了multiprocessing。 multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,>提供了Process、Queue、Pipe、Lock等组件。

需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

Scrapy 框架的介绍

Scrapy,这把刀是我的最爱。

作为一个易上手的高性能爬虫框架,Scrapy 使用 Twisted 异步网络框架处理并发请求。

Scrapy诞生得比较早,核心通过yield这一携程的思路来实现的异步。

虽然 Twisted 框架提供了线程池支持,但是其核心网络部分处理逻辑依赖的是「单线程 IO 多路复用」技术,在 Linux 平台上,是围绕 epoll() 系统调用实现的 Reactor 模式。

总有人说,Scrapy不灵活,限制太多来作为缺点来评价它,但是却没几个人能好好举出究竟具体是什么场景Scrapy不能胜任的案例。

摸棱两可的跟风评价,没有什么营养。

在这里并不是说Scrapy是如何完美的框架,它确实有自己的缺点,但是..

绝大多数情况下,都是因为自己对框架没有足够的了解和自身缺乏开阔的开发思维。

多人协作开发的情况,统一的框架结构,更易于维护。

部分代码

scrapy的爬虫文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# -*- coding: utf-8 -*-
import scrapy
from multiprocessing import Process
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

class BaiduSpider(scrapy.Spider):
name = 'baidu'
allowed_domains = ['baidu.com']

def __init__(self, category=None, *args, **kwargs):
super(BaiduSpider, self).__init__(*args, **kwargs)
self.start_urls = f"https://www.baidu.com/s?ie=utf-8&wd={category}"

def start_requests(self):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
}
yield scrapy.Request(self.start_urls, headers=headers, callback=self.parse)

def parse(self, response):
print(response.status)
# print(response.text)

def start(category):
print(f"category={category}")
settings = get_project_settings()
process = CrawlerProcess(settings)
spider = BaiduSpider()
process.crawl(spider, category=category)
process.start()

if __name__ == "__main__":
name = ['java', 'python', 'C语言', 'php', 'goland']
for i in range(len(name)):
category = name[i]
p = Process(target=start, args=(category,))
p.start()

这段代码要做的是:

  • 使用百度搜索[‘java’, ‘python’, ‘C语言’, ‘php’, ‘goland’]这些关键字
  • 采集搜索到的结果列表。
  • 使用multiprocessing的Process封装,多次运行Scrapy
  • 为每一个关键词,单独跑一个关键词。即’java’跑一个Scrapy,’python’跑一个Scrapy…
  • 而不是一个scrapy跑[‘java’, ‘python’, ‘C语言’, ‘php’, ‘goland’]所有关键词搜索结果

总结

代码中,有get_project_settings和CrawlerProcess方法的应用。

在封装scrapy启动文件的时候,可以更加的优雅。