Scrapy配合socks5 proxy抓取页面

Scrapy是一个非常方便的爬虫框架,功能可以覆盖基本的应用场景,比如多任务、去重、proxy、深度控制、session等等。虽然有些人会认为Scrapy是基于python的,由于python先天的解释性特点,以及GIL的问题,性能不好,但其实爬虫的瓶颈不在性能,在小规模下性能不重要,大规模下瓶颈在带宽也不在性能。这里主要基于Scrapy介绍一下使用socks5 proxy,虽然网上有一些方案介绍,但其实都有问题,此外分享一些使用上的技巧。

Scrapy基本使用

先简单介绍一下Scrapy的基本部署和使用,内容可以参考官网 https://docs.scrapy.org/

部署

基于python的pip安装,最好是使用python3.6以上版本。此外,最好在python的virtualenv环境中创建,使用的库能够独立,方便维护环境。可以参考《利用Python的virtualenv

pip install scrapy

创建项目

执行scrapy命令创建一个example-project的目录。

scrapy startproject example-project

目录中的文件结构比较重要,虽然第一次看上去不太懂,但从简单开始的话,要关注的部分很少。

example-project
	- scrapy.cfg	## 基本不用管。只是scrapy的一些配置内容,很少用到
	- example-project/
		- __init__.py	# 不用管
		- items.py		# 也不用管。文件的目的只是为了将定义的items类放在这个文件,后续多个spider可以共用,
						# 但简单用起来是不需要考虑这些代码结构的优化
		- middleware.py	# 也不用管。集中定义中间件,涉及到请求处理、proxy之类的,简单场景也用不上
		- pipeline.py	# 这个需要关注。因为抓取的内容(items)基本都要放到数据库里面,而写入数据库的操作在这
		- settings.py	# 抓取相关的配置,比如重试、超时、插件等
		- spiders/		# 关键内容,定义爬虫的页面抓取逻辑
			- **.py		# 自己随便取名,定义一个spider逻辑

运行Scrapy

使用scrapy runspider [spider的python脚本路径]来执行。至于spider的脚本如何写,按照官方介绍,以及网上很多的sample,可以很快完成一个spider。

scrapy runspider example-project/spiders/**.py

使用Socks5的proxy

抓取的很多场景会用到代理(proxy),比如要抓取国外网站,但因为慢或者墙的问题;被抓取的站点有ip请求数量控制。Scrapy官方也有介绍使用http proxy,基于Middleware,或者在scrapy.Request或者response.follow中使用meta,比如:

import scrapy


class MyItem(scrapy.Item):
    item1 = scrapy.Field()	# 存放目标抓取元素1
    item2 = scrapy.Field()	# 存放目标抓取元素2


class MySpider(scrapy):
    name = "MySpider"	# 很多场景可以基于Spider的name来区分处理
    allow_domains = ['targetSite.com']	# 目标站点的域名
    start_urls = ['first.targetSite.com', 'second.targetSite.com']	# 起始url,也可以不定义,直接在start_request中控制
	def start_requests(self):
        for url in start_urls:
            ## 使用http proxy来抓取,这里proxyhost:proxyport表示代理的地址和端口
            yield scrapy.Request(url=url, callback=self.parse, meta={'proxy': 'proxyhost:proxyport'})
            
    def parse(self, response, **kwargs):
        item = MyItem()
        item['item1'] = response.xpath("//div[@class='adfa adfdasf']/a/img/@src").get
        next_url = response.xpath("//div[@class='next']/a/@href").get
        yield response.follow(next_url, self.parse_next, cb_kwargs=dict(item=item), meta={'proxy': 'proxyhost:proxyport'})
    
	def parse_next(self, response, item):
        item['item2'] = response.xpath("//div[@id='testid']/span/text()").get()
        yield item

http proxy从哪来,可以从网上购买。但有一些实验性质的,不想花钱买批量的ip或者流量,也许有一个自己搭建的http proxy服务,比如使用tinyproxy、squid来搭建一个http代理是非常简单的。这里也可以使用,但是如果是自己搭建的,除非跟scrapy运行在同一台机器上,如果不是,那么在抓取https链接的时候,会因为证书问题而无法抓取。

如果还是坚持自己部署proxy,可以选择部署一个socks5 proxy,可以绕过https的证书问题。可以参考《Golang实现socks5 proxy服务》部署一个自己的socks5代理服务。但虽然解决了代理访问https的问题,但是Scrapy不支持socks5代理,无法使用以上meta或者Middleware的方式来解决。虽然网上有一些方案是基于txsocksx库来实现一个Middleware,从而在请求处理上hook住,使用socks5的代理交互。但txsocksx只支持python2,目前没有看到有成功的python3适配。

有没有https和scrapy支持的两全其美的scrapy代理方案?也是有的,其实非常tricky的方式,就是http proxy + socks5 proxy合并使用。

  • 在scrapy爬虫本地部署一个http proxy。目的是将本地的请求代理请求一个socks5 proxy,比如可以部署一个privoxy,可以达到这个要求;
  • 远程部署一个socks5 proxy。为了抓取目标站点。

这样,在scrapy的spider中,在meta中配置本地的http proxy,这样就可以解决。