in

十大Python爬虫工具包

十大Python爬虫工具包

Python 是迄今为止用于网络抓取的最流行的语言。它易于学习,拥有庞大的社区和庞大的库生态系统。

在这篇快速概述文章中,我们将看看每个网络抓取工具都应该知道的前 10 个网络抓取包。涵盖各种利基市场,如:

  • HTTP 连接
  • 浏览器自动化
  • HTML 和 JSON 解析
  • 数据质量和验证

Parsel 和 LXML

LXML是一种用于 Python 的快速且功能丰富的 HTML/XML 解析器。它是 C 库的包装器libxml2,是在 Python 中解析 HTML 的最快、最可靠的方法。

LXML 使用强大而灵活的XPath 选择器来查找 HTML 中的元素:

from lxml import html
html = """
<html>
  <title>My Title</title>
  <body>
    <div class="title">Product Title</div>
  </body>
</html>
"""
tree = html.fromstring(html)
print(tree.xpath("//title/text()"))
['My Title']
print(tree.xpath("//div[@class='title']/text()"))
['Product Title']

更进一步,lxmlParsel扩展,它简化了 API 的使用并添加了CSS 选择器支持:

from parsel import Selector

html = """
<html>
  <title>My Title</title>
  <body>
    <div class="title">Product Title</div>
  </body>
</html>
"""

selector = Selector(html)
# use XPath
print(selector.xpath("//title/text()").get())
"My Title"
# or CSS selectors
print(selector.css(".title::text").get())
"Product Title"

Parsel 正在成为解析大量 HTML 文档的实际方式,因为它将速度与lxmlCSS 和 XPath 选择器的易用性结合在一起。

HTTPX

HTTPX是迄今为止最完整、最现代的 Python HTTP 客户端包。它受到流行的请求库的启发,但通过支持现代 Python 和 HTTP 功能,它更进一步。

首先,它支持异步和同步 Python API。这使得扩展基于 httpx 的请求变得容易,同时还可以在简单的脚本中保持可访问性:

import httpx

# use Sync API
with httpx.Client() as client:
    response = client.get("https://web-scraping.dev/product/1")
    print(response.text)

# use Async API:
import asyncio

async def run():
    async with httpx.AsyncClient() as client:
        response = await client.get("https://web-scraping.dev/product/1")
        print(response.text)

asyncio.run(run())

HTTPX 还支持 HTTP/2,它不太可能被阻止,因为大多数真实的人类流量都使用 http2/3:

with httpx.Client(http2=True) as client:
    response = client.get("https://web-scraping.dev/product/1")
    print(response.text)

最后,httpx尊重 HTTP 标准和表单请求,更类似于真正的 Web 浏览器,这可以大大降低被阻止的可能性。就像尊重标题顺序一样。

BeautifulSoup

Beautifulsoup(又名 bs4)是 Python 中的另一个 HTML 解析器库。尽管远不止于此。

与 LXML 和 Parsel 不同,bs4 支持使用可访问的 Pythonic 方法进行解析,例如findand find_all

from bs4 import BeautifulSoup

html = """
<html>
  <title>My Title</title>
  <body>
    <div class="title">Product Title</div>
  </body>
</html>
"""

soup = BeautifulSoup(html, 'lxml')  # note: bs4 can use lxml under the hood which makes it super fast!
# find single element by node name
print(soup.find("title").text)
"My Title"
# find multiple using find_all and attribute matching
for element in soup.find_all("div", class_="title"):
    print(element.text)

对于初学者来说,这种方法比 CSS 或 XPath 选择器更易于访问和可读,并且在处理高度复杂的 HTML 文档时通常更容易维护和开发。

BeautilfulSoup4 还带有许多实用功能,如 HTML 格式化和编辑。例如:

from bs4 import BeautifulSoup

html = """
<div><h1>The Avangers: </h1><a>End Game</a><p>is one of the most popular Marvel movies</p></div>
"""
soup = BeautifulSoup(html)
soup.prettify()
"""
<html>
 <body>
  <div>
   <h1>
    The Avangers:
   </h1>
   <a>
    End Game
   </a>
   <p>
    is one of the most popular Marvel movies
   </p>
  </div>
 </body>
</html>
"""

以及许多其他实用功能,如 HTML 修改、选择性解析和清理。

JMESPath 和 JSONPath

JMESPathJSONPath是两个允许您使用类似于 XPath 的查询语言查询 JSON 数据的库。

随着 JSON 在网络抓取空间中变得越来越大,这些库变得必不可少。

例如,使用 JMESPath,我们可以轻松查询、重塑和展平 JSON 数据集:

data = {
  "people": [
    {
      "name": "Foo", 
      "age": 33, 
      "addresses": [
        "123 Main St", "California", "US"
      ],
      "primary_email": "[email protected]",
      "secondary_email": "[email protected]",
    },
    ...
  ]
}
jmespath.search("""
  people[].{
    first_name: name, 
    age_in_years: age,
    address: addresses[0],
    state: addresses[1],
    country: addresses[2],
    emails: [primary_email, secondary_email]
  }
""", data)
[
  {
    'address': '123 Main St',
    'state': 'California'
    'country': 'US',
    'age_in_years': 33,
    'emails': ['[email protected]', '[email protected]'],
    'first_name': 'Foo',
  },
  ...
]

此功能在抓取隐藏的 Web 数据时特别有用,这可能会导致难以导航和消化的大量JSON 数据集。

或者,JSONPath 不太关注重塑数据集,而更多地关注从复杂的、高度嵌套的 JSON 数据集中选择值,并支持高级匹配函数:

import jsonpath_ng.ext as jp

data = {
    "products": [
        {"name": "Apple", "price": 12.88, "tags": ["fruit", "red"]},
        {"name": "Peach", "price": 27.25, "tags": ["fruit", "yellow"]},
        {"name": "Cake", "tags": ["pastry", "sweet"]},
    ]
}

# find all product names:
query = jp.parse("products[*].name")
for match in query.find(data):
    print(match.value)

# find all products with price > 20
query = jp.parse("products[?price>20].name")
for match in query.find(data):
    print(match.value)

JSONPath 的杀手级功能是递归$..选择器,它允许通过键快速选择数据集中任何位置的值(类似于心爱的 XPath//选择器):

import jsonpath_ng.ext as jp

data = {
    "products": [
        {"name": "Apple", "price": 12.88},
        {"name": "Peach", "price": 27.25},
        {"name": "Cake"},
        {"multiproduct": [{"name": "Carrot"}, {"name": "Pineapple"}]}
    ]
}

# find all "name" fields no matter where they are:
query = jp.parse("$..name")
for match in query.find(data):
    print(match.value)

Playwright 和 Selenium

无头浏览器在网络抓取中变得非常流行,作为处理动态 javascript 和抓取器阻塞的一种方式。

许多网站使用复杂的前端,通过后台请求或 javascript 函数按需生成数据。要访问这些数据,我们需要一个 javascript 执行环境,没有比使用真正的无头 Web 浏览器更好的方法了。

Javascript 指纹识别也正在成为抓取许多现代网站的必要步骤。

因此,为了在 Python 中控制用于网络抓取的无头浏览器,我们有两个流行的库:PlaywrightSelenium

Selenium是最早的主要浏览器自动化库之一,它几乎可以做浏览器可以做的任何事情:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# configure browser
options = Options()
options.headless = True 
options.add_argument("--window-size=1920,1080")
options.add_argument("start-maximized")

driver = webdriver.Chrome(options=options)
driver.get("https://web-scraping.dev/product/1")
# wait for page to load by waiting for reviews to appear on the page
element = WebDriverWait(driver=driver, timeout=5).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, '#reviews'))
)
# click buttons:
button = wait.until(EC.element_to_be_clickable((By.ID, "load-more-reviews")))
button.click()

# return rendered HTML:
print(driver.page_source)

或者,Playwright是浏览器自动化的现代版本,在现代异步和同步 API 中提供所有这些功能:

from playwright.sync_api import sync_playwright

# Using synchronous Python:
with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    context = browser.new_context(viewport={'width': 1920, 'height': 1080})
    page = context.new_page()
    page.goto("https://web-scraping.dev/product/1")

    # wait for the element to be present in the DOM
    page.wait_for_selector('#reviews')

    # click the button
    page.wait_for_selector("#load-more-reviews", state="attached")
    page.click("#load-more-reviews")

    # print the HTML
    print(page.content())

    browser.close()

# or asynchronous
import asyncio
from playwright.async_api import async_playwright

async def run():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context(viewport={'width': 1920, 'height': 1080})
        page = await context.new_page()
        await page.goto("https://web-scraping.dev/product/1")

        # wait for the element to be present in the DOM
        await page.wait_for_selector('#reviews')

        # click the button
        await page.wait_for_selector("#load-more-reviews", state="attached")
        await page.click("#load-more-reviews")

        # print the HTML
        print(await page.content())

        await browser.close()

asyncio.run(run())

Cerberus 和 Pydantic

一个经常被忽视的网络抓取过程是数据质量保证步骤。Web 抓取是一个独特的利基市场,其中结果数据集高度动态,使得质量测试非常困难。

幸运的是,有几种工具可以帮助确保网络抓取数据的质量。

对于需要实时数据验证的网络抓取应用程序,Pydantic是一个不错的选择。Pydantic 允许指定可用于验证和变形抓取数据的严格数据模型:

from typing import Optional
from pydantic import BaseModel, validator

# example for scraped company data
class Company(BaseModel):
    # define allowed field names and types:
    size: int
    founded: int
    revenue_currency: str
    hq_city: str
    hq_state: Optional[str]  # some fields can be optional (i.e. have value of None)

    # then we can define any custom validation functions:
    @validator("size")
    def must_be_reasonable_size(cls, v):
        if not (0 < v < 20_000):
            raise ValueError(f"unreasonable company size: {v}")
        return v

    @validator("founded")
    def must_be_reasonable_year(cls, v):
        if not (1900 < v < 2022):
            raise ValueError(f"unreasonable found date: {v}")
        return v

    @validator("hq_state")
    def looks_like_state(cls, v):
        if len(v) != 2:
            raise ValueError(f'state should be 2 character long, got "{v}"')
        return v

对于需要更大灵活性和不太严格的数据验证的数据网络抓取工具,Cerberus是一个不错的选择。Cerberus 是一个模式验证库,允许使用简单的字典语法指定数据模型:

from cerberus import Validator


def validate_name(field, value, error):
    """function for validating"""
    if "." in value:
        error(field, f"contains a dot character: {value}")
    if value.lower() in ["classified", "redacted", "missing"]:
        error(field, f"redacted value: {value}")
    if "<" in value.lower() and ">" in value.lower():
        error(field, f"contains html nodes: {value}")


schema = {
    "name": {
        # name should be a string
        "type": "string",
        # between 2 and 20 characters
        "minlength": 2,
        "maxlength": 20,
        # extra validation
        "check_with": validate_name,
    },
}

v = Validator(schema)

v.validate({"name": "H."})
print(v.errors)
# {'name': ['contains a dot character: H.']}

Cerberus 的杀手级功能是易用性和定义灵活模式的能力,这些模式可以处理高度动态的网络抓取数据。

Scrapfly Python SDK

Scrapfly 是一个网络抓取 API,它通过各种功能增强网络抓取,例如:

  • 来自50 多个代理国家/地区的住宅代理
  • 防刮保护旁路
  • Javascript 渲染

所有这些都可以通过Python SDK轻松访问,它利用了本文中介绍的工具。
例如,可以使用.selectorScrapfly 结果的属性直接解析 HTML:

from scrapfly import ScrapeConfig, ScrapflyClient
scrapfly = ScrapflyClient(key="YOUR KEY")
result = scrapfly.scrape(ScrapeConfig("https://httpbin.dev/html"))

# scrapfly result build parsel.Selector automatically:
page_title = result.selector.xpath("//h1/text()").get()
"Herman Melville - Moby-Dick"

结    论

在本文中,我们介绍了用于网络抓取的前 10 个 Python 包,它们涵盖了网络抓取过程的几个步骤。

对于网络抓取连接,我们已经介绍了httpx它是一个出色的、功能丰富的 HTTP 客户端。另一方面,有selenium并且playwright使用了一个完整的无头网络浏览器。

对于数据解析,我们了解了三种最流行的 HTML 解析工具:lxml,parselbeautifulsoup. 用于 JSON 解析jmespathjsonpath在网络抓取环境中工作得很好。

最后,对于网络抓取过程的最后一步,我们研究了两种不同的数据验证工具:严格的pydantic和灵活的cerberus

Written by 河小马

河小马是一位杰出的数字营销行业领袖,广告中国论坛的重要成员,其专业技能涵盖了PPC广告、域名停放、网站开发、联盟营销以及跨境电商咨询等多个领域。作为一位资深程序开发者,他不仅具备强大的技术能力,而且在出海网络营销方面拥有超过13年的经验。