前言
网站数据收集方法有很多,比如最基础的requests,简单几行就可以获取网页信息。使用selenium模拟网页点击可以绕过很多反爬策略,编写思路也不同于其他的方法。用scrapy框架来做的话可以清楚地进行目标拆分,并利用内置的线程池可以非常高效地获取信息。
本文以scrapy为目标,总结基础的使用方法,以供后续复习。
配置
本地配置好python及pip后,使用 pip install scrapy
既可以安装scrapy。
基本使用
新建工程
scrapy在使用时,需要在主机命令行里 scrapy startproject <projectname></projectname>
创建一个项目,比如运行 scrapy startproject example
后生成example文件夹,内容如图所示。
; 添加目标网站
命令行也会提示进入example目录,并运行scrapy genspider来创建一个spider。比如运行 scrapy genspider example_spider example.com
,之后会在spiders文件夹下生成一个example_spider.py文件。爬虫的代码就需要写在这个文件内。
GET请求
import scrapy
class ExampleSpiderSpider(scrapy.Spider):
name = 'example_spider'
allowed_domains = ['example.com']
start_urls = ['http://example.com/']
def parse(self, response):
pass
POST请求
import scrapy
class ExampleSpiderSpider(scrapy.Spider):
name = 'example_spider'
allowed_domains = ['example.com']
urls = [
'https://example.com/page/1/',
'https://example.com/page/2/',
]
def start_requests(self):
for target in urls:
yield scrapy.FormRequest(
url=url,
formdata={'arg1':'xxx','arg2':'xxx'},
callback=self.parse,
meta={'arg1':1,'arg2':2}
)
yield scrapy.Request(
url=url,
method='POST',
body = json.dumps({'arg1':'xxx','arg2':'xxx'}),
headers = {'Content-Type':'application/json'},
callback=self.parse,
meta={'arg1':1,'arg2':2}
)
def parse(self, response):
pass
需要注意的是:
name
是爬虫名字,即spidername,之后运行需要指定这个名字。allowed_domains
指定允许爬取的域名,也可以不要。start_urls
指定需要爬取哪些网站,运行时会一个一个向这些网站发请求,并将响应传给parse函数。如果需要动态生成目标网站,可以删掉这个start_urls
变量,并添加一个start_requests(self)
成员函数(需要使用yield scrapy.Request(url = <targetwebsite>, callback=self.parse)</targetwebsite>
作为返回值。爬虫运行时如果发现没有定义start_urls
变量,则会调用这个函数。scrapy.Request
用于发送GET请求。可以添加一个cb_kwargs
参数,它接受一个字典,并可以在parse(self, response, **kwargs)
中通过kwargs
来获取这个字典, 以实现自定义的参数传递。scrapy.FormRequest
用于发送POST请求,请求体放在formdata
中,参数应当都是字符串类型。
这里对官方文档作以修改举例:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'https://quotes.toscrape.com/page/1/',
'https://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse, cb_kwargs={'this_url':url})
def parse(self, response, **kwargs):
page = response.url.split("/")[-2]
url = kwargs['this_url']
filename = f'quotes-{page}.html'
with open(filename, 'wb') as f:
f.write(response.body)
self.log(f'Saved file {filename}')
启动爬取
在最外层的example目录下运行 scrapy crawl <spidername></spidername>
,即可开始爬取。
再以官网文档为例:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'https://quotes.toscrape.com/page/1/',
'https://quotes.toscrape.com/page/2/',
]
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
response.css来提取元素,其含义不言自明。也可以通过response.text获取文本信息。
除了get外,也可以使用extract_first、re_first等函数,详见这里
这里的parse函数yield了一个字典,可以在运行时指定保存文件: scrapy crawl <spidername> -O <output.jl></output.jl></spidername>
来将其保存到文件中,方便后续处理。jl是jsonline即单行json,可以在python中使用简单的文件逐行遍历配合json来处理。其中 -O
表示覆盖输出文件, -o
表示在输出文件后追加。可以添加 -L ERROR
来忽略运行时无关紧要的输出。
对公开API继续爬取时,jl有奇效。
错误处理
可以在 scrapy.Request
与 scrapy.FormRequest
函数中添加一个errback参数,来指定一个自定义的error_handling函数,用法类似于callback。
如下所示:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'https://quotes.toscrape.com/page/1/',
'https://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse, cb_kwargs={'this_url':url}, errback=self.myerr)
def myerr(self, failure):
print(repr(failure))
print(failure.request.url)
print(failure.request.cb_kwargs['this_url'])
def parse(self, response, **kwargs):
url = kwargs['this_url']
进阶使用
缓存(重要)
可以将项目下的 settings.py
里最下面的HTTP_CACHE_XXX启用,来自动将爬下来的数据缓存在本地。当发现xpath写得不对时可以快速更新结果而不用重新请求网站。使用默认的 HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
时,默认会保存在项目的 .scrapy/<crawler name></crawler>
下。之前大致读了下缓存的代码,大致逻辑是根据url、method和body做一个sha1运算,把hexdigest作为请求的hash,哈希前两个十六进制数作为一级目录,完整的hash作为二级目录,每个二级目录下存放meta、pickled_meta、request_body、request_headers、response_body和response_headers等数据。
代理(重要)
有时候爬快了网站会临时ban掉ip,而设置爬取速度不利于大规模爬取(而且有的网站限制会更严格),可以使用代理转发请求。爬国内的网站的话,我自己在用的是快代理的隧道代理,只需要设置下请求的proxy就可以自动切换ip,比较省心。用起来比较稳定(就是有点小贵233),如果有便宜稳定的国内代理欢迎评论!
(没试过它的免费代理 不知道可用性如何)
import base64
username = 'xxxxx'
passwd = 'xxxxx'
proxy_ip = 'xxxx.kdltps.com'
proxy_port = '15818'
meta = {'proxy': f'http://{proxy_ip}:{proxy_port}'}
code = base64.b64encode(f'{username}:{passwd}'.encode()).decode()
headers = {
"Proxy-Authorization": f"Basic {code}",
}
def start_requests(self):
yield scrapy.Request(
headers = headers,
meta = meta,
)
一些注意事项
有时候可能出现url在预设列表里,却爬不下来的情况,参考1,可能要在setting里添加 SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'
Original: https://blog.csdn.net/weixin_43483799/article/details/126014266
Author: c01dkit
Title: 站点数据收集-Scrapy使用笔记
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/791270/
转载文章受原作者版权保护。转载请注明原作者出处!