第 8章 Python 爬虫框架 Scrapy(下)

8.1 Scrapy 对接 Selenium

有一种反爬虫策略就是通过 JS 动态加载数据,应对这种策略的两种方法如下:

 分析 Ajax 请求,找出请求接口的相关规则,直接去请求接口获取数据。

 使用 Selenium 模拟浏览器渲染后抓取页面内容。

8.1.1 如何对接

单独使用 Scrapy 是无法抓取 JS 动态渲染页面的,可以利用 Scrapy 对接 Selenium 来处理 JS。对接的方法也很简单,自定义一个下载中间件,在 process_request()方法中对抓取请求进行处理,启动浏览器进行页面渲染,再将渲染后的结果构造成一个 HtmlResponse对象返回。

8.1.2 对接示例:爬取某网站首页文章

我们通过一个爬取某网站首页文章的例子来进行讲解。首先,打开middlewares.py 文件

from scrapy import signals
from selenium import webdriver
from scrapy.http import HtmlResponse
class JSSeleniumMiddleware:
def __init__(self):
self.browser = webdriver.Chrome()
def __del__(self):
self.browser.close()
def process_request(self, request, spider):
self.browser.get("https://www.jianshu.com/")
return HtmlResponse(url='https://www.jianshu.com/', body=self.browser.page_source,request=request,encoding='utf-8', status=200)

接着在 Settings.py 文件中启用这个下载中间件:

DOWNLOADER_MIDDLEWARES = {
'jianshu.middlewares.JSSeleniumMiddleware': 300,
}

然后,修改我们的爬虫文件,把 parse 中的内容打印出来:

from scrapy import Spider, Request
class JianshuSpider(Spider):
name = 'jianshu'
allowed_domains = ['www.jianshu.com']
start_urls = ['http://www.jianshu.com/']
def start_requests(self):
yield Request('https://www.jianshu.com', callback=self.parse)
def parse(self, response): self.logger.debug(response.body.decode('utf8'))

运行后,可以看到控制台打印出的部分信息如下:

<li id="note-34403329" data-note-id="34403329" class="">
<div class="content">
<a class="title" target="_blank" href="/p/1c0b5f1bf431">为什么月薪2万以上的人从不发
朋友圈?</a>

可以看到 Selenium 渲染 JS 后的页面代码都原封不动地传递过来了,接着我们编写一个JianshuspiderItem 类,以保存文章名、文章部分内容、文章链接和作者名称。

import scrapy

class JianshuItem(scrapy.Item):
title = scrapy.Field()
content = scrapy.Field()
url = scrapy.Field()
nickname = scrapy.Field()

接下来,我们用 Xpath 选择器来提取这些内容,修改爬虫部分代码:

from scrapy import Spider, Request
from ..items import JianshuItem class JianSpider(Spider):
name = "jian"
allowed_domains = ["www.jianshu.com"]
start_urls = ["https://www.jianshu.com"] def start_requests(self):
yield Request('https://www.jianshu.com', callback=self.parse)
def parse(self, response):
li_s = response.xpath('//ul[@class="note-list"]/li')
for li in li_s:
item = JianshuItem()
item['title'] = li.xpath('.//div/a[@class="title"]/text()').extract_first()
item['content'] = str(li.xpath('.//div/p[@class="abstract"]/text()').extract_first()).replace(" ", "").replace("\n", "")
item['url'] = 'https://www.jianshu.com/p/' + str(li.xpath('.//div/a[@class="title"]/@href').extract_first())
item['nickname'] = li.xpath('.//div/a[@class="nickname"]/text()').extract_first()
self.logger.info(str(item))

部分输出结果如下:

[jian] INFO: {'content': '色彩,是地球母亲赋予我们感知大自然最美、最动人的无声语言!色彩不仅和艺术相关,也跟心理学同样有紧密的联系。近年来,心理学领域对色彩的研究主要有...',
'nickname': '疗愈师_茹茵',
'title': '如何通过色彩来看到当下的课题?',
'url': 'https://www.jianshu.com/p//p/0c88d5cdbfed'}
2024-02-13 19:35:00 [jian] INFO: {'content': '影响因子:8.44关于非肿瘤生信,我们也解读过很多,主要有以下类型1单个疾病WGCNA+PPI分析筛选hub基因。2单个疾病结合免疫浸润...',
'nickname': '生信小课堂',
'title': '8.4分非肿瘤生信,免疫浸润+机器学习+动物模型验证。可谓是非肿瘤生信结合实验验证的范文,值得收藏!',
'url': 'https://www.jianshu.com/p//p/f3210edd252e'}

数据提取完成后需要 Item Pipeline 类,将解析的结果保存到 MongoDB 中。打开pipelines.py 文件,自定义一个 MongoPipeline 类,代码如下:

import  pymongo
class MongoPipeline(object):
def open_spider(self, spider):
self.client = pymongo.MongoClient(host='localhost', port=27017)
self.db = self.client['js']
def process_item(self, item, spider):
self.db['index_article'].insert(dict(item))
def close_spider(self, spider):
self.client.close()

接着在 Settings.py 文件中启用 MongoPipeline 类,修改后的配置如下:

ITEM_PIPELINES = {
'jianshu.pipelines.MongoPipeline': 300,
}

把 Spider 解析部分代码的self.logger.info(str(item))修改为 yield item,运行爬虫后,可以看到 MongoDB 中已经保存了文章的相关信息.

另外,每次运行爬虫都会弹出 Chorme 浏览器,有时可能需要把脚本挂到云服务器上,而服务器上是无页面的,这时可以采用 Chrome 的 Headless 模式(Chrome 69 以上版本)。核心代码如下:

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(chrome_options=chrome_options)

8.2 实战:用 Scrapy 实现一个简单的代理池

在爬取站点时,使用代理 IP 是一种非常有效的反反爬虫策略,而代理 IP 的获取又分为免费和收费方式。免费的代理 IP 可以从一些代理站点(如西刺代理等)获取,但是免费的代理 IP 质量非常低,大部分是不能使用的,能使用的代理 IP 寿命

极短,可能几分钟后就不能用了。而收费的代理IP 的价格也不同,高质量的收费代理 IP价格都比较高。

8.2.1 代理池的设计

在开始实现代理池前,我们需要两个核心程序。

  • IP 获取程序:爬取免费代理 IP 站点,解析获得代理 IP,保存到 Redis 数据库中。
  • IP 检测程序:从 Redis 数据库中取出所有代理 IP,访问目标站点看是否可用,不可用的删除。

接着我们要考虑一个问题:IP 获取程序和 IP 检测程序什么时候运行。

如果等我们爬取站点时才去执行的话,效率会非常低,毕竟爬取和检测是一个耗时的过程,对此我们需要另外编写一个调度程序,来调度这两个程序的执行。

还有一个问题是:如何制订调度策略。因为免费代理 IP 的存活期比较短,所以检测 IP可用的程序执行的频度需要高一些,比如每隔 20 秒执行一次,验证代理池里的 IP 是否可用,及时移除失效的代理 IP。

接着是爬取 IP 的程序,当代理池中的可用 IP 少于 10 个时,就会执行一次爬取 IP 的程序。另外,要设置一个保护期,当爬取完 IP 时,在一个时间段(10 分钟)里,不再触发IP 获取的爬虫,除非代理池中已无可用代理,以避免调用过于频繁被免费代理站点 block。

最后就是校验代理 IP 是否可用的方法,即直接访问测试页面,返回码不是 200 或出现超时等异常,则认为此代理 IP 为不可用代理 IP,从代理池中移除。

案例:

https://zxbcw.cn/post/196763/

8.2.2 编写获取代理 IP 的接口

Flask 是一个轻量级的 Python Web 库,安装非常简单,直接通过 pip 命令安装即可,命令如下:

pip install flask

安装后,我们在爬虫项目中新建一个proxy_server.py 文件,创建一个最简单的 Flask实例,代码如下:

from flask import Flask
app = Flask(__name__)
@app.route("/")
def fetch_ip():
return "返回一个代理IP"
if __name__ == '__main__':
app.run()

运行后可以看到控制台输出如下信息:

 Serving Flask app 'proxy_server'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit

运行后,用浏览器访问http://127.0.0.1:5000/,

8.3 用 Scrapyrt 调度 Scrapy

我们现在都是通过命令行输入“scrapy crawl 爬虫名字”来启动一个 Scrapy 爬虫,如果要使爬虫在云服务器上运行,每次都需要先用 SSH 连接服务器,然后启动爬虫,显得非常烦琐,有没有办法来简化这个步骤呢,比如只要通过一个 HTTP 接口就可以完成 Scrapy 的任务调度。

常用的 Scrapy HTTP 接口调度库有两个:Scrapyrt 和 Scrapyrd。前者是轻量级的,如果不需要分布式多任务,可以直接使用 Scrapyrt 来实现 Scrapy 的远程调度。

8.4 用 Docker 部署 Scrapy

8.4.1 Docker 简介

Docker 是基于 Linux 容器的封装,提供了简单易用的容器使用接口。而 Linux 容器是一种虚拟化技术,不是模拟一个完整的系统,而是对进程进行隔离(在进程外嵌套一层),使得进程访问到的各种资源都是虚拟的,从而达到与底层系统隔离的目的。可以简单地将它理解成更轻量级的虚拟机。另外,因为容器是进程级别的,相比虚拟机而言,启动速度更快,资源占用更少。

8.4.2 下载并安装 Docker

Ubuntu 环境

  1. 更新软件包列表: 执行以下命令更新系统软件包列表:
sudo apt update
  1. 安装依赖包: 安装 Docker 所需的依赖包,以及使用 HTTPS 通过 APT 下载软件包所需的工具:
sudo apt install apt-transport-https ca-certificates curl software-properties-common
  1. 添加 Docker 官方 GPG 密钥: 添加 Docker 官方 GPG 密钥以确保下载的软件包是由 Docker 官方签名的
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
  1. 添加 Docker 存储库: 添加 Docker 的官方存储库以获取最新版本的 Docker:
  • 对于 x86_64/AMD64 架构的计算机:
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  • 对于 ARM64 架构的计算机:1. 更新软件包列表: 执行以下命令更新系统软件包列表:
echo "deb [arch=arm64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  1. 安装 Docker Engine: 更新软件包列表后,安装 Docker Engine
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
  1. 启动 Docker 服务: 安装完成后,启动 Docker 服务:
sudo systemctl start docker
  1. 设置 Docker 开机自启动: 如果你希望 Docker 在系统启动时自动启动,可以执行以下命令:
sudo systemctl enable docker
  1. 验证安装: 运行以下命令来验证 Docker 是否已成功安装:
sudo docker run hello-world

8.4.3 创建 Dockerfile

我们通过编写 Dockerfile 文件来定制我们的 Docker 镜像,流程如下。

  1. 导出项目依赖的 Python 环境包

    可以使用 pipreqs 库导出项目依赖的版本号,通过 pip 命令进行安装:
pip install pipreqs

安装后命令行在爬虫目录下,输入下述命令生成 requirements.txt 文件:

pipreqs ./

另外有一点要注意,Windows 系统执行上述操作会报编码错误(UnicodeDecodeError: 'gbk' codec can't decode byte 0xa8 in position 24: illegal multibyte sequence),在命令后指定编码格式即可:

pipreqs ./ --encoding=utf8

运行成功后稍等片刻,输出如下信息,代表导出完成:

INFO: Successfully saved requirements file in ./requirements.txt

如果想导出整个系统的依赖库,可以使用下述命令:

pip freeze > requirements.txt
  1. 编写 Dockerfile 文件

    在项目根目录下创建 Dockerfile 文件,切记没有后缀,内容如下:
FROM python:3.6
ENV PATH /usr/local/bin:$PATH
ADD . /code
WORKDIR /code
RUN pip3 install -r requirements.txt
CMD scrapy crawl BingWallpaper

介绍这六行代码的作用

- FROM:使用 Docker 基础镜像,在此基础上运行 Scrapy 项目。
- ENV:环境变量配置,将/usr/local/bin:$PATH 赋值给 PATH。
- ADD:将本地代码放置到虚拟容器中,第一个参数“.”代表本地当前路径;第二个参数“/code”代表虚拟容器中的路径,就是把项目中的所有内容放到虚拟容器的/code 目录下。
- WORKDIR:指定工作目录。
- RUN:执行某些命令来做一些准备工作,如安装库。
- CMD:容器启动命令,当容器运行时,会执行此命令,这里我们通过命令来启动爬虫。

8.4.4 构建 Docker 镜像

配置完后,执行下述命令来构建 Docker 镜像:

docker build -t bing:latest .

执行过程中部分输出如下:

Sending build context to Docker daemon 5.979MB
Step 1/6 : FROM python:3.6
---> 7a35f2e8feff
Step 2/6 : ENV PATH /usr/local/bin:$PATH
---> Using cache
---> c5a031f0b181
Step 3/6 : ADD . /code
---> 1315d8135233
Step 4/6 : WORKDIR /code
---> Running in ef69980444b1
Removing intermediate container ef69980444b1
---> 735a489cd4e8
Step 5/6 : RUN pip3 install -r requirements.txt
---> Running in e81518234d70
...
Successfully built 24df73bf5460
Successfully tagged quotes:latest

至此 Docker 镜像构建完成,可以通过命令查看构建的镜像信息:

REPOSITORY TAG IMAGE ID CREATED SIZE
bing latest 70b57b1cfcb9 12 seconds ago 1GB

我们可以试着本地运行这个 Docker 镜像,输入下述命令:

docker run quotes

8.4.5 把生成的 Docker 镜像推送到 Docker Hub

镜像构建完成后,我们可以把它推送到 Docker 镜像托管平台,镜像就可以在远程服务器上运行了,打开 Docker Hub 官网(https://hub.docker.com/) ,单击 Create Repository 创建一个镜像,命名为 bing。

在本地为我们的镜像打一个标签,命令示例如下:

docker tag bing:latest coderpig/bing:latest

接着使用下述命令把镜像推送到 Docker Hub 上:

docker push coderpig/bing

等待推送完毕,单击 Tags 选项卡可以看到刚上传的镜像。

8.4.6 在云服务器上运行 Docker 镜像

镜像上传完了,接着我们在云服务器上下载这个镜像并启动,输入下述命令:

docker run coderpig/bing

运行后可以看到,不需要配置 Python 环境处理版本冲突等问题,下载完成后完成爬取。

以上就是使用 Docker 部署 Scrapy 的基本方法。

使用此类脚本下载网站内容时应遵守网站的使用条款,以及相关的法律法规。

本系列文章皆做为学习使用,勿商用。

第 8章 Python 爬虫框架 Scrapy(下)的更多相关文章

  1. 教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神

    本博文将带领你从入门到精通爬虫框架Scrapy,最终具备爬取任何网页的数据的能力.本文以校花网为例进行爬取,校花网:http://www.xiaohuar.com/,让你体验爬取校花的成就感. Scr ...

  2. 【转载】教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神

    原文:教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神 本博文将带领你从入门到精通爬虫框架Scrapy,最终具备爬取任何网页的数据的能力.本文以校花网为例进行爬取,校花网:http:/ ...

  3. Python爬虫框架Scrapy教程(1)—入门

    最近实验室的项目中有一个需求是这样的,需要爬取若干个(数目不小)网站发布的文章元数据(标题.时间.正文等).问题是这些网站都很老旧和小众,当然也不可能遵守 Microdata 这类标准.这时候所有网页 ...

  4. Linux 安装python爬虫框架 scrapy

    Linux 安装python爬虫框架 scrapy http://scrapy.org/ Scrapy是python最好用的一个爬虫框架.要求: python2.7.x. 1. Ubuntu14.04 ...

  5. Python爬虫框架Scrapy实例(三)数据存储到MongoDB

    Python爬虫框架Scrapy实例(三)数据存储到MongoDB任务目标:爬取豆瓣电影top250,将数据存储到MongoDB中. items.py文件复制代码# -*- coding: utf-8 ...

  6. 《Python3网络爬虫开发实战》PDF+源代码+《精通Python爬虫框架Scrapy》中英文PDF源代码

    下载:https://pan.baidu.com/s/1oejHek3Vmu0ZYvp4w9ZLsw <Python 3网络爬虫开发实战>中文PDF+源代码 下载:https://pan. ...

  7. 《精通Python爬虫框架Scrapy》学习资料

    <精通Python爬虫框架Scrapy>学习资料 百度网盘:https://pan.baidu.com/s/1ACOYulLLpp9J7Q7src2rVA

  8. Python爬虫框架Scrapy

    Scrapy是一个流行的Python爬虫框架, 用途广泛. 使用pip安装scrapy: pip install scrapy scrapy由一下几个主要组件组成: scheduler: 调度器, 决 ...

  9. Python爬虫框架Scrapy获得定向打击批量招聘信息

    爬虫,就是一个在网上到处或定向抓取数据的程序,当然,这样的说法不够专业,更专业的描写叙述就是.抓取特定站点网页的HTML数据.只是因为一个站点的网页非常多,而我们又不可能事先知道全部网页的URL地址, ...

  10. Python爬虫框架Scrapy安装使用步骤

    一.爬虫框架Scarpy简介Scrapy 是一个快速的高层次的屏幕抓取和网页爬虫框架,爬取网站,从网站页面得到结构化的数据,它有着广泛的用途,从数据挖掘到监测和自动测试,Scrapy完全用Python ...

随机推荐

  1. AAC编解码移植之基本简介

    一 概念 AAC是高级音频编码(Advanced Audio Coding)的缩写,出现于1997年,最初是基于MPEG-2的音频编码技术.由Fraunhofer IIS.Dolby Laborato ...

  2. 生成文件名为系统时间的C源码实例

    一 最近遇到了一个需要根据时间记录文件名的.先写一个实例来实战: #include<stdlib.h> #include<time.h> #include<stdio.h ...

  3. .NET Core使用 CancellationToken 取消API请求

    您是否曾经访问过一个网站,它需要很长时间加载,最终你敲击 F5 重新加载页面. 即使用户刷新了浏览器取消了原始请求,而对于服务器来说,API也不会知道它正在计算的值将在结束时被丢弃,刷新五次,服务器将 ...

  4. 3DCAT+上汽奥迪:打造新零售汽车配置器实时云渲染解决方案

    在 5G.云计算等技术飞速发展的加持下,云渲染技术迎来了突飞猛进的发展.在这样的背景下,3DCAT应运而生,成为了业内知名的实时云渲染服务商之一. 交互式3D实时云看车作为云渲染技术的一种使用场景,也 ...

  5. SqlServer查询表的所有字段属性及其是否是主外键

    CREATE PROC [dbo].[sp_help2] @TableName VARCHAR(50) = NULL AS SET NOCOUNT ON SET TRANSACTION ISOLATI ...

  6. KingbaseES Create Index Concurrently 过程探究

    前言: 我们知道Oracle 可以通过create index online 在线创建索引,而不影响其他会话修改数据,但Oracle 实际在online 创建索引的最后一步,实际还是需要进行锁升级,申 ...

  7. #dp,齐肯多夫定理#CF126D Fibonacci Sums

    题目 \(T(T\leq 10^5)\) 组数据,每次给定数字 \(n(n\leq 10^{18})\), 问有多少种方案将 \(n\) 分解成若干个互不相同的斐波那契数 分析 如果找到一个方案使得所 ...

  8. #折半搜索,状压dp#nssl 1471 Y

    分析 设\(dp[i][j][s]\)表示从\(i\)到\(j\)的一条路径状态为\(s\)是否存在 但是这样肯定会T掉,考虑拼凑路径,分成两部分, 设\(dp[0/1][s]\)分别表示以某个起点/ ...

  9. #深搜,期望#CF105B Dark Assembly

    洛谷题目传送门 CODEFORCES传送门 分析 题目强调贿赂要在投票开始前完成说明分糖和成功率可以分开计算 那么分糖考虑直接暴搜,由于题目并没有说糖必须全部分完, 所以每一次分完一颗糖后均要求当前状 ...

  10. mysql系列之杂谈(一)

    从刚开始工作到现在,除了实习的时候在国企用过oracle,毕业之后陪伴我的数据库一直都是mysql,而由于mysql的开源特性,也让成为无数公司的宠儿,越走越远. 我们在刚开始使用mysql时,会发现 ...