写点什么

基于 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:002464

评论

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

【云图说】 | 第268期 初识开天企业工作台MSSE

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 企业号 3 月 PK 榜

为企业全方位解决进销存管理难题的ERP套件

力软低代码开发平台

使用metrics-server监控k8s的资源指标

tiandizhiguai

DevOps 微服务 云原生 k8s

新思科技发布《2023年开源安全和风险分析》报告

InfoQ_434670063458

开源 新思科技 软件安全

ChatGPT辅助编程

鲸品堂

ChatGPT 企业号 3 月 PK 榜

开源项目的演进会遇到哪些“坑”?KubeVela 从发起到晋级 CNCF 孵化的全程回顾

阿里巴巴云原生

阿里云 开源 云原生 KubeVela

Apache Flink 1.16 功能解读

Apache Flink

大数据 flink 实时计算

Apache IoTDB v1.0.1 发布|修复分区计算,优化集群启停流程

Apache IoTDB

IoTDB

首批!阿里云容器服务 ACK 顺利通过信通院云原生混部项目评估

阿里巴巴中间件

阿里云 容器 云原生

Soul 云原生网关最佳实践

阿里巴巴中间件

阿里云 云原生 实践 云原生网关

详细剖析|袋鼠云数栈前端框架Antd 3.x 升级 4.x 的踩坑之路

袋鼠云数栈

前端‘’

引领云数仓创新浪潮 HashData闪耀PostgreSQL中国技术大会

酷克数据HashData

基于Python+UIautomation的WindowsGUI自动化测试实战(二)-计算器

Python 自动化测试 GUI UIaotumation

数据库革新拐点已来——MatrixOne Beta Program Recap

MatrixOrigin

云原生 分布式数据库 MatrixOrigin MatrixOne

小程序容器作为软件中间件技术不可忽视的价值

FinFish

小程序容器 小程序技术 软件中间件

隐私计算技术路线介绍及对比

隐语SecretFlow

隐私计算

Apache IoTDB v0.13.4 发布|优化监控功能

Apache IoTDB

IoTDB

瓴羊Quick BI与Power BI之争:数据大屏优势明显

对不起该用户已成仙‖

新思科技为三星SDS公司开源使用和风险管理提供自动治理解决方案

InfoQ_434670063458

开源 软件开发 新思科技 软件安全

ICLR 2023 | 网易伏羲3篇论文入选,含强化学习、自然语言处理等领域

网易伏羲

Tuxera NTFS2023版读写NTFS磁盘功能工具

茶色酒

Tuxera NTFS2023

基于Python+UIautomation的WindowsGUI自动化测试实战(一)-记事本

自动化测试 GUI UIaotumation

ChatGPT潜能很大,问题也是

引迈信息

人工智能 低代码开发 应用开发 ChatGPT JNPF

重磅通知!OpenAI又放大招:官宣开放API接口-3.5版本 需求大涨,机遇与挑战并存,谁能拔得头筹?

加入高科技仿生人

人工智能 开源 openai ChatGPT

软件测试/测试开发 | 一步一步学测试平台开发-Vue restful请求

测试人

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

下一站,冠军|走进2022 OceanBase数据库大赛12强

OceanBase 数据库

数据库 oceanbase

「资源广场」上线|以开发者为中心,打造开放资源共享平台

Jianmu

jenkins CI/CD 镜像仓库 容器镜像 建木

what量化合约系统开发&源码丨clear合约量化系统开发技术(Demo案例)

I8O28578624

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