2025上半年,最新 AI实践都在这!20+ 应用案例,任听一场议题就值回票价 了解详情
写点什么

基于 Scrapy 的爬虫解决方案

  • 2021-06-23
  • 本文字数:4948 字

    阅读完需:约 16 分钟

基于Scrapy的爬虫解决方案

一、背景介绍


笔者在业务中遇到了爬虫需求,由于之前没做过相关的活儿,所以从网上调研了很多内容。但是互联网上的信息比较杂乱,且真真假假,特别不方便,所以完成业务后就想写一篇对初学者友好且较为完整的文章,希望能对阅读者有所帮助。


由于笔者最近 Python 用得比较熟练,所以就想用 Python 语言来完成这个任务。经过一番调研,发现 Scrapy 框架使用者比较多,文档也比较全,所以选择了使用该框架。(其实 Scrapy 只做了非常简单的封装,对于普通的爬虫任务,使用 requests 库和 bs4 库中的 BeautifulSoup 类就完全能解决了)。


首先简单介绍一下爬虫是什么。爬虫就是从一个或多个 URL 链接开始,使用某种方法(例如 requests 库中的函数)获取到该 URL 对应的网页的内容(一般是 HTML 格式),然后从该网页的内容中提取出需要记录下来的信息和需要继续爬取的 URL 链接(例如使用上文中提到的 BeautifulSoup 类)。之后,再对爬取到的 URL 链接进行上述同样的操作,直到所有 URL 链接都被爬取完,爬虫程序结束。


Scrapy 的官网【1】,英文版官方文档【2】,第三方的汉化文档(较为简陋和过时)【3】提供如下,感兴趣的读者也可以自行查阅。由于本文重点不在这里,就不在此处对 Scrapy 进行介绍了。


【1】:https://scrapy.org/

【2】:https://docs.scrapy.org/en/latest/

【3】:https://scrapy-chs.readthedocs.io/zh_CN/0.24/index.html


二、Scrapy 使用方法


安装 Scrapy 库


pip install scrapy
复制代码


新建一个爬虫项目


scrapy startproject your_project_name
复制代码


输入该命令后,会在当前目录下新建一个名为 your_project_name 的文件夹,该文件夹下的文件层级关系如下:


your_project_name|    scrapy.cfg|----your_project_name|    |    __init__.py|    |    items.py|    |    middlewares.py|    |    pipelines.py|    |    settings.py|    |----spiders|    |    |    __init__.py
复制代码


其中,scrapy.cfg 是整个项目的配置文件,spiders 目录下存放爬虫的逻辑代码,因为该项目刚建立,还没有写具体的爬虫代码,所以该目录下为空。


生成一个爬虫


在刚刚新建的项目目录下输入命令:


scrapy genspider example www.qq.com
复制代码


其中 example 是爬虫的名字,www.qq.com 是该爬虫的第一个要爬取的 URL 链接。


执行该命令后,Scrapy 会在 spiders 目录下生成一个叫 example.py 的文件,该文件是一个非常基础的爬虫模板。之后要做的事情就是在该 py 文件里填入具体的爬虫逻辑代码,然后再执行该爬虫脚本就可以了。example.py 文件内的代码如下:


import scrapy

class ExampleSpider(scrapy.Spider): name = 'example' allowed_domains = ['qq.com'] start_urls = ['http://qq.com/']
def parse(self, response): pass
复制代码


代码中的 ExampleSpider 就是刚才生成的爬虫类。其中,name 是爬虫的名字,allowed_domains 是对域名的限制(即该爬虫只会爬取该限制下的 URL 域名),start_urls 是爬虫的初始 URL 链接,这里面的值是刚才创建爬虫时输入的 URL 链接,parse 函数是默认的解析函数。


运行爬虫


在项目目录下执行命令:


scrapy crawl example


其中 example 是要运行的爬虫名字。执行该命令后,该框架就会用 example 爬虫里定义的初始 URL 链接和解析函数去爬取网页了。


调试爬虫


在写代码的过程中,由于不同网页的源码的组织方式不同,所以需要用一种交互式的方式来访问网页,以此来修改代码。虽然在很多情况下可以通过 Chrome 浏览器 F12 的审查模式来查看网页的 HTML 源码,但是在有些情况下代码中获得的源码和浏览器中看到的却是不一样的,所以交互式访问网页就必不可少了。(也可以通过运行完整爬虫的方式来调试代码,但是效率就有点低下了)。


要想交互式访问网页,需要在项目目录下执行命令:


scrapy shell www.qq.com


使用体验类似于直接在命令行输入 python 进入 Python 的交互式界面。


完善解析函数


解析函数的完善是爬虫的核心步骤。解析函数的初始化如下:


def parse(self, response):    pass
复制代码


其中只有 response 一个实参,该实参就是访问某个 URL 链接的返回结果,里面含有该 URL 链接的 HTML 源码(该 response 是对 requests.Response 类的封装,所以用法类似,但是包含的成员函数更多)。而解析函数 parse 的作用就是从 response 中杂乱的 HTML 源码提取出有价值的信息。


在 Scrapy 框架中,有两种解析 HTML 源码的函数,分别是 css 和 xpath。其中 css 是 Scrapy 专有的函数,具体用法只能在 Scrapy 文档中查找,不建议使用;而 xpath 是一种通用的语言(例如 BeautifulSoup 类中也能使用),它的一些语法的定义在网上资料更多。xpath 的具体用法要讲的话就太多了,所以这里不多做介绍,如果有需要,可以直接去搜索引擎查找相关资料。


如果需要在解析过程中遇到了需要解析的 URL 链接,则可以直接调用:


yield scrapy.Request(url_str, callback=self.parse)
复制代码


其中,url_str 是需要解析的 URL 链接的字符串,self.parse 是解析函数,这里我使用的是默认的解析函数,当然这里也能使用自定义的解析函数(自定义解析函数的入参出参类型需要和默认解析函数相同)。


值得注意的是:scrapy.Request 除了以上俩必须的参数外,还能通过 meta 字段来传递参数,而参数的获取能通过 response.meta 来实现。


小建议


默认情况下,Scrapy 会遵守被爬取网站的 robots.txt 规则(该文件规定了哪些能爬,哪些不能爬),但往往我们想要爬取的内容都被规定为不能爬取的内容。可以将 settings.py 文件中的 ROBOTSTXT_OBEY = True 改为 ROBOTSTXT_OBEY = False 来避免这种情况的发生。


三、常见问题


动态网页不能正确解析


上述的简单操作只能解析静态网页,需要动态加载的网页(例如含有 Javascript 代码的网页)则无法正常解析,因为 response 里的 HTML 源码是动态加载之前的页面的源码,而我们需要的大多是动态加载之后的页面。


可以通过在 Python 中调用 Chrome 浏览器的方式来处理这个问题。除此之外,还能使用 Chrome 浏览器的 headless 模式。使用了该模式之后,Chrome 浏览器并不会真的被调用,但是 Python 中能获取到和浏览器相同的返回结果,而浏览器中返回的结果就是动态加载之后的页面。


不过,要使用这个方法,必须在机器上安装 Chrome 浏览器和对应版本的 Chrome 驱动程序。安装完成之后,在 middlewares.py 文件中加入以下代码:


from selenium import webdriverfrom scrapy.http import HtmlResponse

class JavaScriptMiddleware: def process_request(self, request, spider): option = webdriver.ChromeOptions() option.add_argument('--headless') option.add_argument('--no-sandbox') option.add_argument('--disable-gpu') driver = webdriver.Chrome(options=option, executable_path=chrome_driver_path_str) driver.get(request.url) js = 'var q=document.documentElement.scrollTop=10000' driver.execute_script(js) body = driver.page_source return HtmlResponse(driver.current_url, body=body, encoding='utf-8', request=request)
复制代码


除此之外,还要在 settings.py 文件中加入以下代码:



DOWNLOADER_MIDDLEWARES = { 'your_project_name.middlewares.JavaScriptMiddleware': 543,}
复制代码


经过这两处修改之后,爬虫脚本里的所有 request 请求都会通过 Chrome headless 浏览器包装后再发向要爬取的 URL 链接。


防爬虫之修改 header


很多网站都有各自的反爬虫机制,但是最基础的一种方式是检查请求的 HTTP 包里面的 header 是否正常。其中经常检查的一个字段是 User-Agent,User-Agent 字段指的是浏览器的型号。反爬虫机制会检查该字段是否为普通的浏览器,而普通的爬虫程序是不会修饰该字段的。如果不显式将该字段设为某种浏览器型号,就容易触发反爬虫,从而不能正常地获得数据。


要想修改 Scrapy 里的 user-agent 字段,可以在 settings.py 文件里添加以下代码:


USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36'
复制代码


添加完该代码后,Scrapy 在发起 request 请求时就会将上面的值替换到 header 中的 User-Agent 中。


反爬虫之 IP 池


在很多时候,爬取网站时一开始是能正常获得数据的,但是爬着爬着,就不能正常地获得数据了。一个很大的可能是 IP 被该网站封禁了。每个网站封 IP 的策略都不一样,但是总体来说其实就是该 IP 访问该网站的频率太高,网站害怕该访问是恶意攻击或者担心服务器承受不了大量的访问从而直接封禁该 IP。


应对方式也非常粗暴,那就是用代理 IP 去爬虫。网站封一个 IP,我就用另外的 IP 去访问,只要我 IP 足够多,就总能获取到我想要的所有数据。而正好互联网上就有服务商提供这种 IP 服务。网上大致分为免费和付费两种服务,其中免费提供商提供的 IP 质量非常低,有不小的概率是直接不能用的,所以这里不推荐使用免费服务。至于付费服务商网上有很多家都挺靠谱的,本文里使用的名为“快代理”的服务商,下面提供的代码也是只针对该特定厂家的。不同服务商使用 IP 池的方式都不一样,具体使用方法还是以各自的官方文档为主。


在“快代理”上购买 IP 套餐后,在 middleware.py 文件中添加一下代码:


from w3lib.http import basic_auth_headerimport requests

class ProxyDownloaderMiddleware: username = 'your_username' password = 'your_password' api_url = 'https://dps.kdlapi.com/api/getdps/?orderid=your_orderid&num=1&pt=1&dedup=1&sep=1' proxy_ip_list = [] list_max_len = 20
def update_ip(self): if len(self.proxy_ip_list) != self.list_max_len: ip_str = requests.get('https://dps.kdlapi.com/api/getdps/?orderid=your_orderid&num={}&pt=1&dedup=1&sep=3'.format(self.list_max_len)).text self.proxy_ip_list = ip_str.split(' ') while True: try: proxy_ip = self.proxy_ip_list.pop(0) proxies = { 'http': 'http://{}:{}@{}'.format(self.username, self.password, proxy_ip), 'https': 'http://{}:{}@{}'.format(self.username, self.password, proxy_ip) } requests.get('http://www.baidu.com', proxies=proxies, timeout=3.05) self.proxy_ip_list.append(proxy_ip) return except Exception as e: self.proxy_ip_list.append(requests.get(self.api_url).text)
def process_request(self, request, spider): self.update_ip() request.meta['proxy'] = 'http://{}'.format(self.proxy_ip_list[-1]) # 用户名密码认证 request.headers['Proxy-Authorization'] = basic_auth_header(self.username, self.password) return None
复制代码


其中 username,password,order id 都是“快代理”中使用 IP 所要用的参数。上面的代码维护了一个大小为 20 的 IP 池,每次要使用时就提取第一个 IP 并先要检查该 IP 是否已经失效,如果失效了就丢弃并补充新的 IP。Scrapy 每次发起 request 请求时,会经过该 proxy 层的封装,但要想正常使用,还得在 settings.py 文件中添加以下代码:


DOWNLOADER_MIDDLEWARES = {    'your_project_name.middlewares.ProxyDownloaderMiddleware': 100,}
复制代码


在上文爬取动态页面的相关内容中也修改了这个 DOWNLOADER_MIDDLEWARES 这个字典。该字典中的 key 和 value 分别是在 middlewares.py 文件中添加的类和封装 request 包的顺序。如果要同时使用动态页面爬取和 IP 池,那么 settings.py 文件的该参数应该如下所示:


DOWNLOADER_MIDDLEWARES = {    'your_project_name.middlewares.JavaScriptMiddleware': 543,    'your_project_name.middlewares.ProxyDownloaderMiddleware': 100,}
复制代码


其中 100 < 543,代表 request 请求要先经过代理封装再经过动态加载封装,最后才发送给目标 URL 链接。



头图:Unsplash

作者:赵宇航

原文:https://mp.weixin.qq.com/s/-jCxnhzo-G9fzZNT-Azp7g

原文:基于 Scrapy 的爬虫解决方案

来源:云加社区 - 微信公众号 [ID:QcloudCommunity]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021-06-23 08:002437

评论

发布
暂无评论
发现更多内容

使用验证码拦截爬虫和机器人实践分享

宙哈哈

php html 爬虫 机器人

从零学习SDK(1)什么是SDK,为什么要使用它

MobTech袤博科技

MobPush推送查询API

MobTech袤博科技

助力企业数字化转型,「一体化」或是最佳实践路径

ToB行业头条

软件测试/测试开发丨测试步骤代码修改,用 Yaml实现数据驱动

测试人

软件测试 自动化测试 yaml 测试开发 UI自动化测试

KgCaptcha接入汇总

宙哈哈

Java php Python C# html

Flink Table Store 独立孵化启动 , Apache Paimon 诞生

Openlab_cosmoplat

大数据 开源

聚焦用户精细化运营场景,极客邦科技与火山引擎数智平台达成合作

字节跳动数据平台

用户增长 数字化 用户运营 数字化案例 企业号 4 月 PK 榜

大模型打开了一层技术天花板,催生新场景变革老场景

中关村科金

人工智能 企业服务 大模型 对话式AI

浙江宁波|2023年度宁波市甬江引才工程

科兴未来News

[验证码] KgCaptcha风险监测方法

宙哈哈

php html

软件测试/测试开发丨实战演练基于加密接口测试测试用例设计

测试人

软件测试 自动化测试 测试开发

博睿学院 | 本周四:ETL技术在数据标准化中的应用实践

博睿数据

ETL 智能运维 博睿数据 博睿学院

CodeGeeX 130亿参数大模型的调优笔记:比FasterTransformer更快的解决方案

Openlab_cosmoplat

Python 开源社区

揭秘 Milvus 助力平台建设的终极奥义

Zilliz

Meetup Milvus 向量数据库

阿里云弹性计算资深技术专家徐海弘:云上自动化运维成熟度模型

云布道师

弹性计算

如何在 Web 实现支持虚拟背景的视频会议

声网

Web 视频会议 RTE 虚拟背景

传输体积下降 85%,融云 HTTP 压缩算法解析

融云 RongCloud

算法 音视频 传输 融云 通讯

Birdwatcher 进阶使用指南

Zilliz

Milvus 向量数据库 birdwatcher

通过 NFTScan 追踪 NFT 钻石手持仓

NFT Research

NFT NFTScan

FastAPI 开发中数据校验利器 Pydantic 介绍与集成使用

宇宙之一粟

Python 后端 FastApi Pydantic

流媒体数字版权应用实践

马说IT

数字版权 流媒体

量化交易场景下日增 144 万条数据,使用 MySQL 和 TDengine 分别如何建模?

TDengine

大数据 tdengine 物联网 时序数据库

基于二代征信报告的信用评估模型实践

中关村科金

金融 征信 风控 对话式AI

浙江宁波|2023年上半年宁波市镇海区高层次人才项目政策申报

科兴未来News

尚硅谷新版Git视频教程发布

小谷哥

ZRTC高并发策略在专属音视频中台场景的应用

中关村科金

RTC 中关村科金 音视频中台 高并发策略 对话式AI

MIAOYUN与OpenCloudOS、TencentOS Server 3完成产品兼容互认证

MIAOYUN

容器云 云平台 产品兼容性互认 互认证 兼容性互认证

低代码有哪些缺点?4千字深入解析

优秀

低代码 低代码缺点

「刷起来」Go必看的进阶面试题详解

王中阳Go

golang 高效工作 学习方法 面试题 Go 语言

DataLeap数据资产实战:如何实现存储优化?

字节跳动数据平台

MySQL 数据库 大数据 数据治理 数据存储

基于Scrapy的爬虫解决方案_文化 & 方法_云加社区_InfoQ精选文章