Python 爬虫 入门: 常见 工具 介绍

的 我 的 上 一篇 文章 《网页 爬虫 完全 指南》 , 这篇 文章 将 涵盖 几乎 所有 的 Python 网页 爬 取 工具。 我们 从 最基本 的 开始 讲起 逐步 涉及 到 当前 最 前沿 的 技术 , 并且 对 它们 它们 的利弊 进行 分析。

当然 , 我们 不能 全面 地 介绍 每个 工具 , 但 这篇 文章 应该 足以 让 你 很好 地 知道 哪些 工具 做 什么 , 以及 何时 每 一种 一种 工具。

注意: 本文 中 所 涉及 到 的 Python 均 指 Python3。

要点 要点:

  • Ιστός 基础
  • Ocket 创建 一个 υποδοχή 并且 发送 HTTP 请求
  • urllib3 & LXML
  • αιτήματα & BeautifulSoup
  • Scrapy (爬虫 框架)
  • Σελήνιο (浏览 器 自动化 测试 框架 Chrome & Chrome —— χωρίς κεφάλι
  • 总结

Ιστός 基础

互联网 其实 是非常 复杂— — 通过 通过 浏览 浏览 一个 简单 的 网页 时 , 其 背后 其实 涉及 到 许多 技术 和 概念。 并不 打算 对其 进行 逐一 讲解, 但 我 会 告诉 你 如果 想要 从 网络 网络 中爬 取 数据 需要 了解 哪些 最 重要 的 知识。

Πρωτόκολλο μεταφοράς υπερκειμένου (超 文本 传输 协议 , 简称 HTTP)

HTTP 采用C / S 模型, 在 HTTP 客户 机 (如 浏览 器 , Python 程序, curl (命令 行 工具) , Requests 等等) 创建 一个 连接 并向 HTTP 服务器 (如 Nginx , Apache 等) 发送 信息 (“我 想浏览 产品 页 ")。

如 服务器 返回 一个 (如 HTML 代码) 并且 关闭 连接。 与 FTP 这些 有 状态 协议 不同 , HTTP 的 每个 事务 都是 的 , 因此 被 称为 无 状态 协议。

, , 当 你 在 浏览 器 中 键入 网站 地址 时 , HTTP 请求 如下 所示:

GET /product/ HTTP/1.1 Host: example.com Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/web\ p,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch, br Connection: keep-alive User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit\ /537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 

在 这个 请求 的 第一 行, 你 可以获得 如下 的 信息:

  • 使用 Λήψη 动词 或者 方法, 意味着 我们 从 指定 的 路径/product/请求 数据。 还有 其他 HTTP 谓词 , 你 可以 在 这里 看到 完整 的 列表。
  • HTTP 协议 的 版本。 在 本 中 , 我们 将 重点 讨论 HTTP1。
  • 多个 κεφαλίδα 字段。

的 是 最 重要 的 κεφαλίδα 字段:

  • Κεντρικός υπολογιστής:服务器 的 域名。 如果 没有 给出 端口 号 , 则 默认 为 80 * . *
  • Παράγοντας χρήστη:包含 有关 发起 请求 的 客户 端 的 信息, 包括 OS 等 信息。 比如说 上面 的 例子 中, 表明 了 我 的 浏览 (Chrome) , 在 Mac OS X 系统 上. 这个 κεφαλίδα 字段 很 重要 , 因为 它 要么 用于 统计 的 移动 和 用户 访问 我 , 移动 用于 桌面 网站) , 要么 用于 防止 的 任何 违规 行为。 因为 这些 报 头 是 由 客户 端 发送 的 , 所以 可以 使用 使用 一种名为 «报 头 欺骗» 的 技术 对其 进行 修改。 这 正是 我们 的 爬虫 要做 的 , 使 他们 看起来 像 一个 正常 的 网页 浏览 器。
  • Αποδοχή:表明 响应 可接受 的 内容 类型。 有 许多 不同 内容 内容 类型 和 子text : κείμενο / απλό , κείμενο / html, εικόνα / jpeg, εφαρμογή / json
  • ** Cookie: ** name1 = value1; όνομα2 = τιμή2… 这个 κεφαλίδα 字段 包含一组 键值 对。 这些 称为 会话 cookie , 是 网站 用来 验证 用户 身份 和 在 浏览 器 中 数据 的 工具。 比如说 , 当 你 时 时 填写 完 账号 密码 并 提交Ie 服务器 会 检查 你 输入 的 账号 密码 是否 正确。 如果 无误, 它将 重定向 并且 在 你 的 浏览 器 中 注入 一个 会话 cookie , 浏览 器 会 将此 cookie 连同 随后 的 每个 请求 一起 发送 给 服务器。
  • Παραπομπή : 这个 字段 实际 实际 URL 的 URL。 网站 通过 此 κεφαλίδα 字段 来 判断 用户 的 来源 , 进而 调整 该 用户 的 权限。 例如, 很多 新闻 网站 付费 订阅 , 只 允许 你 浏览 10% 的 帖子。 但是 但是D 如果 用户 是 像 Reddit 这样 的 新闻 聚合 器 , 就能 浏览 全部 内容。 网站 使用 r 字段 来 来 进行 这 一点。 有时 , 我们 不得不 伪造 这个 κεφαλίδα 字段 来 获取 我们 想要 提取 的 内容 内容。

当然 κεφαλίδα 字段 不仅仅是 这些。 你 可以 在 此处 获取 更多 的 信息。

服务器 将 返回 类似 如下 的 响应:

HTTP/1.1 200 OK Server: nginx/1.4.6 (Ubuntu) Content-Type: text/html; charset=utf-8     ...[HTML CODE] 

TP 第一 行 能 看到 一个 HTTP200 OK代码。 这 意味着 我们 的 请求 成功 了。 至于 请求 头 , 有 很多 HTTP 代码 , 分为 四个 常见 的 类: 2XX 用于 成功 请求 , 3XX 用于 重定向 , 4XX 用于 错误 请求 (最著名 的 是 404 未 找到) , 5XX 用于 服务器 错误。

使用 你 使用 Web 浏览 器 发送 HTTP 请求 , 它将 解析 HTML 代码 , 获取 所有 最终 资源 (JavaScript 、 CSS 和 图像 文件) , 并将 结果 呈现 到 主 窗口 中。

使用 下一节 中 , 将 将 使用 y Python 执行 HTTP 请求 的 不同 方法 , 并 从 响应 中 提取 我们 想要 的 数据。

Ocket 创建 一个 υποδοχή 并且 发送 HTTP 请求

Υποδοχή (套接字)

Ocket Python 中 执行 HTTP 请求 的 最基本 方法 是 打开 一个 υποδοχή 并 手动 发送 HTTP 请求:

import socket HOST = 'www.google.com' # Server hostname or IP address PORT = 80 # Port client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (HOST, PORT) client_socket.connect(server_address) request_header = b'GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n' client_socket.sendall(request_header) response = '' while True: recv = client_socket.recv(1024) if not recv: break response += str(recv) 

有了 我们 有了 HTTP 响应 , 从 其中 提取 数据 的 最基本 方法 就是 使用 正 则 表达式。

正 则 表达式

(则 表达式 (RE, ή Regex) 是 字符串 的 搜索 模式。 你 可以 使用 regex 在 更大 的 文本 中 搜索 特定 的 字符 单词 , 例如 , 你 可以 识别 网页 上 的 所有 电话 号码。 你 也 轻松 轻松 地 轻松的 字符串 , 例如 , 可以 将 格式 较差 的 HTML 中 的 所有 大写 标记 用 小写 标记 替换。 还 可以 验证 一些 输入。

在进行 可能 想 知道 , 为什么 在进行 Web 抓取 时 了解 正 则 表达式 很 重要? 毕竟 , 有 各种 不同 的 Python 模块 来 解析 HTML 、 XPath 和 CSS 选择 器。

的 一个 理想 的 语义 世界 中 , 数据 很 容易 被 机器 读取 , 信息 被 到 到 相关 的 HTML 元素 和 具有 一定 意义 的 属性 中。

但 现实 世界 混乱 的 , 你 经常 会 在 元素 中 搜索 大量 的 文本。 当 你 想要 在 这个 巨大 的 文本 块 中 提取 特定 数据 (价格 、 日期 或 名称) 时 , 你 必须 使用 正 则 表达式 表达式 表达式

注意:这篇 文章 只 介绍 了 一 小 部分 你 做 事情。 你 表达式 做 这篇 文章 来 练习 正 表达式 , 以及 通过 这个 则 表达式 , 以及 通过 这个 很棒 的 博客 来 进一步 了解。。

当 你 的 数据 类似于 下面 这种 的 时候 , 正 则 表达式 就能 发挥 很大 的 作用:

Price : 19.99$

Ath 可以 使用 XPath 表达式 选择 这个 文本 节点 , 然后 使用 这种 regex 提取 τιμή。 请 记住 , 正 则 表达式 模式 是 从左到右 的 , 每个 源 字符 只 使用 一次。:

^Price\s:\s(\d+.\d{2})$ 

提取 提取 HTML 标记 中 的 文本 , 使用 regex 是 很 烦人 的 , 但 它 是 可行 的:

import re html_content = '

Price : 19.99$

'

Ocket 你 所见 , ocket υποδοχή 手动 发送 HTTP 请求 并 使用 正 则 表达式 解析 响应 是 可以 完成 的 , 但这 很 复杂。 所以 更高 级别 级别 的 可以 可以 使 这个 任务 变得 更 容易 容易。

urllib3 & LXML

说明: 我们 y Python 中 的 urllib 系列 的 库 的 时候 很 容易 感到 迷茫 。Python 除了 有 作为 标准 库 一部分 的 urlib 和 urlib2 , 还有 urlib3。urllib2 在 Python3 中 被 分成 很多 模块 3 3 3 3 3 3 3 3 3 , , Ll 成为 标准 库 的 一部分。 其实 应该 有 这些 令人 的 的 细节 这些 令人 困惑 的 细节 , 在 本篇 中 , 我 选择 只 ll urllib 3 , 因为 它 在 Python 世界 中 被 广泛 使用。。

urllib3 是 一个 高级 包 , 它 允许 你 对 HTTP 请求 做 任何 你 想做 的 事情。 我们 用 更少 的 代码 行 来 完成 上面 的 套接字 操作:

import urllib3 http = urllib3.PoolManager() r = http.request('GET', '//www.google.com') print(r.data) 

比套接字版本要简洁得多,对吗?不仅如此,API 也很简单,你可以轻松地做许多事情,比如添加 HTTP 头、使用代理、发布表单等等。

例如,如果我们必须设置一些 header 字段来使用代理,我们只需这样做:

import urllib3 user_agent_header = urllib3.make_headers(user_agent="") pool = urllib3.ProxyManager(f'', headers=user_agent_header) r = pool.request('GET', '//www.google.com/') 

看见没?完全相同的行数。

然而,有些事情 urllib 3并不容易处理。如果要添加 cookie,则必须手动创建相应的 header 字段并将其添加到请求中。

此外,urllib 3 还可以做一些请求不能做的事情,比如池和代理池的创建和管理,以及重试策略的控制。

简单地说,urllib 3 在抽象方面介于请求和套接字之间,尽管它比套接字更接近请求。

为了解析响应,我们将使用 lxml 包和 XPath 表达式。

XPath

XPath 是一种使用路径表达式在 XML 或 HTML 文档中选择节点或节点集的技术。与文档对象模型(DocumentObjectModel)一样,XPath 自 1999 年以来一直是 W3C 标准。即使 XPath 本身不是一种编程语言,它允许你编写可以直接访问特定节点或节点集的表达式,而无需遍历整个 XML 或 HTML 树。

可以将 XPath 看作一种专门用于 XML 或 HMTL 的正则表达式。

要使用 XPath 从 HTML 文档中提取数据,我们需要 3 件事:

  • HTML 文档
  • 一些 XPath 表达式
  • 运行这些表达式的 XPath 引擎

首先,我们将使用我们通过 urllib 3 获得的 HTML。我们只想从 Google 主页中提取所有链接,所以我们将使用一个简单的 XPath 表达式 //a ,并使用 LXML 来运行它。LXML 是一个快速易用的 XML 和 HTML 处理库,支持 XPath。

安装 :

pip install lxml 

下面是前面片段之后的代码:

from lxml import html 

输出如下:

//books.google.fr/bkshp?hl=fr&tab=wp //www.google.fr/shopping?hl=fr&source=og&tab=wf //www.blogger.com/?tab=wj //photos.google.com/?tab=wq&pageId=none //video.google.fr/?hl=fr&tab=wv //docs.google.com/document/?usp=docs_alc ... //www.google.fr/intl/fr/about/products?tab=wh 

请记住,这个示例非常简单,并没有向你展示 XPath 有多强大。 (注意: 这个 XPath 表达式应该更改为 //a/@href 为了避免在 links 中迭代以获得它们的 href )。

如果你想了解更多关于 XPath 的知识,可以阅读这个很棒的介绍文档。这个 LXML 文档 也编写得很好,很适合基础阅读。.

XPath 表达式和 regexp 一样很强大,是从 HTML 中提取信息的最快方法之一。虽然 XPath 也像 regexp 一样很快就会变得凌乱,难以阅读和维护。

requests & BeautifulSoup(库)

下载量已经超过 11,000,000 次的 Requests 库是 Python 包中的佼佼者,它是 Python 使用最广泛的包。

安装:

pip install requests 

使用 Requests 库发送一个请求是非常容易的事情:

import requests 

使用 Requests 库可以很容易地执行 POST 请求、处理 cookie 和查询参数。

Hacker News 认证

假设我们想要创建一个工具来自动提交我们的博客文章给 Hacker News 或任何其他论坛如 Buffer。在提交我们的链接之前,我们需要认证到这些网站。这就是我们要通过 Requests 和 BeautifulSoup 做的事情!

下面是 Hacker News 登录表单和相关的 DOM:

这个表单上有三个 标签。第一个带有 hidden 类型的名字为 “goto” 输入,另外两个是用户名和密码。

如果你在 Chrome 浏览器中提交表单,你会发现有很多事情发生:重定向和正在设置 cookie。这个 cookie 将由 Chrome 在每个后续请求上发送,以便服务器知道你已通过身份验证。

通过 Requests 来做这些工作将会变得非常简单,它将自动为我们处理重定向,而处理 cookie 则可以使用 _Session_Object 完成。

接下来我们需要的是 BeautifulSoup,它是一个 Python 库,它将帮助我们解析服务器返回的 HTML,以确定我们是否登录。

安装:

pip install beautifulsoup4 

所以我们要做的就是通过 POST 请求将这三个带有我们登录凭证的输入发送到 /login 终端,并且验证一个只在登录成功时才出现的元素。

import requests from bs4 import BeautifulSoup BASE_URL = '//news.ycombinator.com' USERNAME = "" PASSWORD = "" s = requests.Session() data = {"gogo": "news", "acct": USERNAME, "pw": PASSWORD} r = s.post(f'{BASE_URL}/login', data=data) 

我们可以尝试提取主页上的每一个链接,以了解更多关于 BeautifulSoup 的信息。

顺便说一句,Hacker News 提供了一个功能强大的 API,所以我们这里只是以它为例,你真的应该直接使用 API,而不是爬取它! _

我们需要做的第一件事是观察分析Hacker News主页,以了解我们必须选择的结构和不同的 CSS 类。

我们可以看到所有的 posts 都在 里 ,因此,我们需要做的第一件事是选择所有这些标记。通过下面这行代码我们很容易实现:

links = soup.findAll('tr', class_="athing") 

然后,对于每个链接,我们将提取其 ID、标题、url 和排名:

import requests from bs4 import BeautifulSoup r = requests.get('//news.ycombinator.com') soup = BeautifulSoup(r.text, 'html.parser') links = soup.findAll('tr', class_="athing") formatted_links = [] for link in links: data = { 'id': link['id'], 'title': link.find_all('td')[2].a.text, "url": link.find_all('td')[2].a['href'], "rank": int(links[0].td.span.text.replace('.', '')) } formatted_links.append(data) 

正如你所看到的,Requests 和 BeautifulSoup 是提取数据和自动化实现各种操作(如填写表单)的很好的库。如果你想做大规模的网络抓取项目,你仍然可以使用请求,但你需要自己处理很多事情。

在爬取大量网页的时候,你需要处理很多事情:

  • 找到并行化代码的方法,使代码更高效
  • 处理错误
  • 存储爬取的数据
  • 过滤和筛选数据
  • 控制请求速度,这样就不会使服务器超载

幸运的是,我们可以使用工具处理所有这些事情。

Scrapy

scrapy 是一个强大的 Python Web 抓取框架。它提供了许多特性来异步下载、处理和保存网页。它处理多线程、爬行(从链接到查找网站中的每个URL的过程)、站点地图爬行等等。

Scrapy 还有一个名为 ScrapyShell 的交互模式。你可以使用 ScrapyShell 快速测试代码,比如 XPath 表达式或 CSS 选择器。

Scrapy 的缺点在于陡峭的学习曲线——有很多东西要学。

继续上述 Hacker News 的例子,我们将编写一个 ScrapySpider,它会抓取前 15 页的结果,并将所有内容保存在 CSV 文件中。

pip 安装 Scrapy:

pip install Scrapy 

然后,你可以使用 scrapycli 生成项目的样板代码:

scrapy startproject hacker_news_scraper 

hacker_news_scraper/spider 我们将使用Spider的代码创建一个新的 Python 文件:

from bs4 import BeautifulSoup import scrapy class HnSpider(scrapy.Spider): name = "hacker-news" allowed_domains = ["news.ycombinator.com"] start_urls = [f'//news.ycombinator.com/news?p={i}' for i in range(1,16)] def parse(self, response): soup = BeautifulSoup(response.text, 'html.parser') links = soup.findAll('tr', class_="athing") for link in links: yield { 'id': link['id'], 'title': link.find_all('td')[2].a.text, "url": link.find_all('td')[2].a['href'], "rank": int(link.td.span.text.replace('.', '')) }

Scrapy中有很多规定.这里我们定义了一个启动URL数组。属性name将用于使用Scrapy命令行调用我们的Spider。

start_urls 数组中的每个URL调用解析方法

然后,为了让我们的爬虫更好的在目标网站上爬数据,我们要对Scrapy进行微调。

# Enable and configure the AutoThrottle extension (disabled by default)See //doc.scrapy.org/en/latest/topics/autothrottle.html AUTOTHROTTLE_ENABLED = True The initial download delay 

你应该一直把这个爬虫程序一直运行着,它将通过分析响应时间和调整并发线程的数量来确保目标网站不会因为爬虫而负载过大。

你可以使用 ScrapyCLI 运行下面的代码并且设置不同的输出格式(CSV、JSON、XML等)。

scrapy crawl hacker-news -o links.json 

类似于这样,最终爬取的结果会按照 json 的格式导出到一个名为 links 的 json 文件中

Selenium & Chrome —headless

对于大规模的网络抓取任务来说,Scrapy 是非常好的。但是,如果需要爬取用 JavaScript 框架编写的单页应用程序,这是不够的,因为它无法呈现 JavaScript 代码。

爬取这些 SPAs 是很有挑战性的,因为经常涉及很多 Ajax 调用和 WebSocket 连接。如果性能存在问题,你将不得不逐个复制 JavaScript 代码,这意味着使用浏览器检查器手动检查所有网络调用,并复制和你感兴趣的数据相关的 Ajax 调用。

在某些情况下,涉及到的异步 HTTP 调用太多,无法获取所需的数据,在 headless 模式的浏览器中呈现页面可能会更容易。

另一个很好的用例是对一个页面进行截图。这是我们将要对 Hacker News 主页做的事情(再次!)pip 安装 Selenium 包:

pip install selenium 

需要 还 需要 Chromedriver:

brew install chromedriver 

从 , 我们 只需 en Selenium 包 中 导入 Webriver , 配置 Chrome 的 Headless = True , 并 设置 一个 窗口 大小 否则 否则 它 非常 小):

from selenium import webdriver from selenium.webdriver.chrome.options import Options 

然后 你 应该 得到 一个 很好 的 主页 截图。

使用 可以 en SeleniumAPI 和 Chrome 做 更多 的 事情 , 比如:

  • 执行 JavaScript
  • 填写 表单
  • 点击 元素
  • 用 CSS 选择 器 或 XPath 表达式 提取 元素

Σελήνιο 和 χωρίς κεφάλι less 下 的 的 是 完美 组合 , 能 爬 取 到 任何 你 想要 爬 取 的 数据。 你 可以 将 使用 的 的 Chrome 的 器 所做 的 所有 操作 都 设置 为 自动化。

Chrome 最大 的 缺点 是 需要 大量 的 CPU / CPU 能力。 通过 一些 微调 , 你 可以 将 每个 Chrome 每个 的 内存 占用 减少 到 300-400MB , 但 每个 实例 仍然 需要 一个 CPU 核心。

多个 你 想 同时 运行 多个 Chrome 实例 , 你 将 需要 强大 的 服务器 (其 成本 迅速 上升) , 以及 持续 监视 资源 资源

总结

的 希望 这个 概述 将 帮助 你 选择 你 的 Python 抓取 工具 , 并 希望 你 通过 本文 能 有所 收获。

爬虫 在 这篇 文章 中 所 介绍 的 工具 都是 我 在 做 自己 的 项目 ScrapingNinja 时 用过 的 , 这个 项目 很 简单 的 网络 爬虫 API。

这篇 文章 中 提及 的 每 一个 工具 , 我 之后 都会 单独 写 一篇 博客 来 进行 深入 阐述 其 细节。

不要 犹豫 , 在 评论 中 告诉 我 你 还想 知道 关于 爬虫 的 哪些 知识。 我 将会 在下 一篇 文章 中 进行 讲解 分析。

Καλό ξύσιμο!

原文 : //www.freecodecamp.org/news/web-scraping-101-in-python/ , 作者: Pierre de Wulf