使用Python进行网络抓取:初学者的实用指南

使用Python进行网络抓取:初学者指南

Scrapeless Scraping 浏览器为需要浏览器输出而非初始HTML的Python抓取工作流程呈现JavaScript驱动的公共页面。

TL;DR

  • 一个基本的Python抓取器有四个阶段:请求、解析、选择和存储。 将每个阶段保持分开,因此错误容易被定位。
  • 从一个你被允许访问的稳定页面开始。 在编写选择器或循环之前检查响应。
  • HTML解析器不执行JavaScript。 在所需内容仅在页面脚本运行之后出现时,使用基于浏览器的路径。
  • 选择器是数据契约的一部分。 更倾向于语义标签、标签、稳定属性和URL模式,而不是生成的类名。
  • 负责任的抓取是有限制和透明的。 尊重访问规则、条款、隐私、版权以及对网站施加的操作负载。

使用Python进行网络抓取意味着什么

使用Python进行网络抓取意味着获取网络资源并将返回的内容转换为结构化数据。一个小抓取器可能会读取一个HTML页面并提取其标题。一个生产收集器可能会渲染JavaScript,遵循允许的分页,验证记录,存储来源,并监视源结构的变化。

Python是一个常见的选择,因为它有成熟的HTTP、解析、浏览器、数据和存储库。该语言仅是一个层次。一个可靠的抓取器还需要清晰的目标架构、源策略、与稳定页面特征匹配的选择器,以及区分真实数据与错误页面或空壳的检查。

从公共内容和狭窄目标开始。“从一个允许的页面收集标题和规范URL”比“抓取整个网站”更容易验证。狭窄版本揭示了机制,而不鼓励无限制的爬行。

四阶段抓取模型

阶段问题典型Python工具
请求服务器是否返回了预期的资源?urllib.request或Requests
解析响应是否可以表示为文档树?html.parser、Beautiful Soup或lxml
选择哪些元素映射到所需字段?CSS选择器、标签搜索、属性或XPath
存储如何保存干净的记录和来源?csv、json、sqlite3或数据库客户端

在学习时,不要将这些阶段合并为一个不透明的函数。在解析之前打印状态、内容类型、最终URL和简短的响应预览。在解析之后,检查所选择的元素,然后再输出。阶段边界使得丢失的标题是否来自网络访问、JavaScript渲染、选择器更改或数据清理变得明显。

Python urllib.request文档 涵盖了标准库请求接口。第三方 Requests文档 提供了更符合人体工程学的HTTP API,具有会话、头部、解码、代理和超时。

一个可运行的第一个抓取器

这个示例仅使用Python的标准库和位于example.com的稳定演示页面。它获取页面,检查响应类型,解析第一个标题,并打印一个JSON记录。代码没有凭据,也没有特定于网站的访问变通方法。

import json
from html.parser import HTMLParser
from urllib.request import Request, urlopen


class FirstHeadingParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.in_h1 = False
        self.heading_parts = []

    def handle_starttag(self, tag, attrs):
        if tag == "h1" and not self.heading_parts:
            self.in_h1 = True

    def handle_endtag(self, tag):
        if tag == "h1":
            self.in_h1 = False

    def handle_data(self, data):
        if self.in_h1:
            self.heading_parts.append(data.strip())


url = "https://example.com/"
request = Request(url, headers={"User-Agent": "LearningScraper/1.0"})

with urlopen(request, timeout=15) as response:
    content_type = response.headers.get_content_type()
    html_text = response.read().decode(response.headers.get_content_charset() or "utf-8")

if content_type != "text/html":
    raise ValueError(f"Expected HTML, received {content_type}")

parser = FirstHeadingParser()
parser.feed(html_text)
record = {"url": url, "heading": " ".join(parser.heading_parts)}
print(json.dumps(record, indent=2))

预期的标题是“示例域”。脚本使用描述性的用户代理、超时、内容类型检查和显式解码。这些细节虽然小,但在目标变得不太可预测时建立了重要习惯。

使用Requests和Beautiful Soup

Requests和Beautiful Soup缩短了相同的工作流程。Requests获取响应;Beautiful Soup解析HTML并支持CSS样式选择。官方 Beautiful Soup文档 解释了解析器选择、标签搜索、CSS选择器和树导航。

import requests
from bs4 import BeautifulSoup

url = "https://example.com/"
response = requests.get(
    url,
    headers={"User-Agent": "LearningScraper/1.0"},
    timeout=15,
)
response.raise_for_status()

soup = BeautifulSoup(response.text, "html.parser")
heading = soup.select_one("h1")

if heading is None:
    raise ValueError("The page did not contain an h1 element")

print({"url": response.url, "heading": heading.get_text(" ", strip=True)})

在虚拟环境中安装依赖项,使用 python -m pip install requests beautifulsoup4在一个真实项目中锁定版本,以便新环境解决相同的依赖集。当爬虫增长时,保持请求和解析代码分开。

选择持久的选择器

语义元素

标题、文章、时间、价格标签或规范链接通常比视觉包装类更清晰地表达意思。

稳定的属性

文档化的数据属性、项目属性、可访问的标签和ID可以超越生成的样式类。

URL模式

具有持久路径形状的链接可以支持在可见卡片更改布局时的发现。

结构化页面数据

公共JSON或嵌入的结构化数据在获得访问权限时可能更好地保留字段名称而不是表示标记。

选择器应尽可能具体,不应过于宽泛。六个嵌套类的链容易被打破。一个裸 div 选择器过于广泛。存储一个来自授权来源的小HTML固定装置,并测试所需字段是否仍然存在,而可选字段可以为null。

绝不要假设第一个匹配的元素是正确的。检查标签、父上下文、单位和规范URL。如果价格可以表示销售、标价或分期,架构应保留区别,而不是将每个值强加到一个字段中。

静态HTML与动态页面

请求和urllib检索服务器响应,但不运行页面JavaScript。如果浏览器显示的数据在 response.text中缺失,则页面可能在加载后获取或构建该内容。通过查看初始来源和呈现的文档确认差异。

动态页面并不总是需要浏览器自动化。页面可能调用一个公共JSON端点,该端点由网站文档记录或暴露给浏览器。当直接使用被允许且稳定时,结构化数据可以更容易地进行验证。当内容依赖于交互、客户端呈现或可见状态时,使用浏览器并等待有意义的元素,而不是模糊的延迟。

Scrapeless爬虫浏览器为JavaScript呈现和交互提供受管理的浏览器会话。Python应用程序可以通过支持的浏览器框架或Scrapeless集成连接,收集呈现的内容,然后重新使用其正常的解析和验证阶段。

清洁和存储记录

提取的字符串通常需要规范化。去除周围的空格,保留有意义的内部空格,解析带有其货币或单位的数字,并在转换可能丢失信息时保持原始源值。将缺失的可选字段表示为null,而不是虚构的零或空声明。

记录应携带来源:源URL、可用时的规范URL、字段值,以及与数据集相关的时间或源版本。对于小项目,JSON Lines很方便,因为每一行都是一个独立的对象。CSV适用于平面记录。SQLite增加数据类型、索引、唯一性约束和查询,而无需服务器。

在存储之前验证。所需字段应存在,URL应为绝对URL,枚举值应与架构匹配,数字范围应合理。在稳定的源标识符中去重,而不是在可能更改的显示标题中去重。

负责任且可维护的爬虫

负责任的爬虫始于权限和范围。审查网站的条款、爬虫指令、适用法律、版权、隐私义务和任何官方API。仅收集为声明目的所需的公共字段。未经授权请勿访问私人、机密、受限或经过身份验证的材料。

限制工作量。在适当的地方缓存未更改的页面,避免不必要的重复请求,并保持适度的并发。当上下文允许时识别客户端。在收集个人数据之前保护所收集的数据,并定义保留和删除规则。

可维护性来自可观察的合同。记录最终的URL、状态、内容类型、选择器计数和验证失败,而不存储秘密。为代表性HTML添加测试。当选择器更改时,检查新页面,并有意更新提取规则,而不是用空输出隐藏失败。

从脚本到生产管道

生产爬虫分离调度、获取、解析、验证、存储和监控。每个阶段都有明确的输入和输出。这使得在不重写字段验证的情况下用浏览器呈现替换静态请求成为可能,或在不触及选择器的情况下更改存储。

  1. 定义架构和允许的源范围。
  2. 捕获一个代表性的响应并验证它是预期的页面。
  3. 编写选择器和字段级验证。
  4. 仅在一个页面工作后添加有限的分页。
  5. 以每条记录存储来源和稳定的标识符。
  6. 监控缺失的字段、选择器计数、响应类型和源变更。
  7. 随着工作流扩展,审查访问政策和数据保留。

在学习时保持样本小。一个产生十个正确、可追溯记录的爬虫比一个收集几千个未经验证的字符串的爬虫更具基础性。

结论

使用Python进行网页爬虫是一个可观察阶段的序列:请求、解析、选择、验证和存储。从一个允许的静态页面开始,使用稳定的选择器,保留来源,并测试您生成的记录。仅在内容需要呈现或交互时添加浏览器,并随着项目的扩展保持访问、隐私、工作量和维护约束的明确。

准备超越静态HTML吗?

将您的Python数据工作流连接到管理的浏览器呈现,以获取动态公共页面。

立即注册并获得 $5的免费信用无需信用卡.

领取您的$5信用 →

常见问题

使用Python进行网页爬虫合法吗?

网络爬虫并没有一个普遍适用的规则。合法性依赖于管辖权、数据、访问方法、合同条款、版权、隐私和预期用途。处理公共数据,审查网站的条款和机器人指令,在适当情况下优先选择官方API,并在重大项目中咨询合格的律师。

Beautiful Soup和Requests会运行JavaScript吗?

不。Requests检索HTTP响应,而Beautiful Soup解析接收到的HTML或XML。两者都不会执行页面JavaScript。当所需内容只有在渲染或交互后才能出现时,请使用授权的结构化端点或基于浏览器的工作流程。

初学者应该先抓取什么?

从一个稳定的演示页面或明确允许预期用途的网站开始。从一页中提取一两个字段,验证它们,并保存一小条记录。在学习过程中避免抓取账户数据、个人数据和广泛爬取。

我怎么知道选择器是否可靠?

可靠的选择器与字段的含义对应,并在具有代表性的页面上保持稳定。优先选择语义标签、标签、文档属性和持久的URL模式。测试所需的和可选的字段,确保与已保存的授权样本一致,并在所需元素消失时可见地失败。

Python爬虫何时应该使用托管浏览器?

当目标内容需要JavaScript渲染、滚动、导航或交互,而直接的HTTP响应无法提供时,请使用托管浏览器。在Python应用程序中即使获取转移到浏览器服务,也要保持解析、验证、来源和访问政策。

参考文献