先决条件
在阅读本文之前,您需要具备 Python 的基础知识,并且已在计算机上安装了 Python。此外,您还需要安装 Requests 和 BeautifulSoup 模块。您可以在命令提示符或终端中运行以下命令来完成安装:
$ pip install requests bs4
在本文的第二部分,我们将使用 Scrapy 构建一个示例网络爬虫,届时您需要安装 Scrapy 框架。该框架的开发者强烈建议您在专用的虚拟环境中安装 Scrapy,以避免与系统包发生冲突。
建议您安装 virtualenv 和 virtualenvwrapper 来创建一个隔离的 Python 环境。请注意,virtualenvwrapper 有一个名为 virtualenvwrapper-win 的 Windows 版本。
此外,您还需要通过 pip 安装 pipx 才能安装 virtualenv。
$ python -m pip install --user pipx
$ python -m pipx ensurepath
创建隔离的 Python 环境后,您可以使用以下命令安装 Scrapy。
$ pip install Scrapy
什么是网络爬虫?
网络爬取和网页抓取是相关但不同的概念。网页抓取是指从网站中提取数据的整体过程。网络爬取则是自动遍历网页以查找需要抓取的 URL 的具体任务。
网络爬虫通常从一组待访问的 URL 列表(即“种子”)开始。在遍历每个 URL 时,它会搜索 HTML 中的链接,并根据特定标准进行筛选。发现的任何新链接都会被添加到队列中以备后续处理。随后,提取的 HTML 或指定信息会被传递到另一个处理管道中进行进一步处理。
在构建网络爬虫时,需注意并非网站上的所有页面都会被访问。实际访问的页面数量取决于爬虫的资源预算、爬取深度或执行时间限制。
许多网站都包含一个 robots.txt 文件,用于指定网站哪些部分可以被爬取,哪些部分应被排除。此外,部分网站还提供 sitemap.xml 文件,其指示比 robots.txt 更为明确,不仅会具体告知爬虫应爬取哪些页面,还会为每个 URL 提供额外的元数据。
网络爬虫通常用于多种目的:
- SEO分析工具除了收集HTML内容外,还会采集响应时间和响应状态等元数据,用于检测失效页面以及跨域链接,从而收集反向链接。
- 价格监控工具会爬取电商网站以查找产品页面并提取元数据,特别是价格信息。随后会定期回访这些产品页面。
- 搜索引擎(如 Googlebot、Bingbot 和 Yandex Bot)会收集网络上大部分内容的 HTML 信息,并利用这些数据使其可被搜索。
本文后文将对比两种在 Python 中构建网络爬虫的方法。第一种方法是使用 Requests 库发送 HTTP 请求,并借助 BeautifulSoup 解析 HTML 内容;第二种方法则是使用网络爬虫框架,我们将采用 Scrapy。
使用 Requests 和 BeautifulSoup 库
Python 中的 requests 模块是用于发送 HTTP 请求的强大工具。若将其用于网络爬虫,您可以先导入该模块,然后向特定 URL 发送请求。例如:
url = 'https://amazon.com/s?k=baby+products'
response = requests.get(url)
获取响应后,您可以使用 BeautifulSoup 从 HTML 内容中提取所有链接。例如:
import json
from urllib.parse import urljoin
from bs4 import BeautifulSoup
html = response.text
links = []
soup = BeautifulSoup(html, 'html.parser')
for link in soup.find_all('a'):
path = link.get('href')
if path and path.startswith('/'):
path = urljoin(url, path)
links.append(path)
print(json.dumps(links, sort_keys = True, indent = 2))
随后,您可以遍历这些链接并向其发送请求,重复此过程直至爬取完所有目标页面。以下是一个实现此功能的递归函数:
import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup
import logging
logging.basicConfig(
format='%(asctime)s %(levelname)s:%(message)s',
level=logging.INFO)
url = 'https://amazon.com/s?k=baby+products'
visited = []
def crawl(url):
logging.info(f'Crawling: {url}')
visited.append(url)
html = ''
try:
html = requests.get(url).text
except Exception:
logging.exception(f'Failed to crawl: {url}')
return
soup = BeautifulSoup(html, 'html.parser')
# here you can extract and store useful data from the page
for link in soup.find_all('a'):
path = link.get('href')
if path and path.startswith('/'):
path = urljoin(url, path)
if path not in visited:
crawl(path)
crawl(url)
该函数会为访问的每个 URL 记录一行日志。
2023-01-16 09:20:51,681 INFO:Crawling: https://amazon.com/s?k=baby+products
2023-01-16 09:20:53,053 INFO:Crawling: https://amazon.com/ref=cs_503_logo
2023-01-16 09:20:54,195 INFO:Crawling: https://amazon.com/ref=cs_503_link
2023-01-16 09:20:55,131 INFO:Crawling: https://amazon.com/dogsofamazon/ref=cs_503_d
2023-01-16 09:20:56,549 INFO:Crawling: https://www.amazon.com/ref=nodl_?nodl_android
2023-01-16 09:20:57,071 INFO:Crawling: https://www.amazon.com/ref=cs_503_logo
2023-01-16 09:20:57,690 INFO:Crawling: https://www.amazon.com/ref=cs_503_link
2023-01-16 09:20:57,943 INFO:Crawling: https://www.amazon.com/dogsofamazon/ref=cs_503_d
2023-01-16 09:20:58,413 INFO:Crawling: https://www.amazon.com.au/ref=nodl_&nodl_android
2023-01-16 09:20:59,555 INFO:Crawling: None
2023-01-16 09:20:59,557 ERROR:Failed to crawl: None
虽然基础网页爬虫的代码看似简单,但要成功爬取整个网站,必须克服许多挑战。其中包括以下问题:
- 下载 URL 的逻辑缺乏重试机制,且当 URL 数量庞大时,URL 队列的效率较低。
- 爬虫未进行身份标识,且忽略 robots.txt 文件。
- 爬虫运行缓慢且不支持并行处理。每个 URL 的爬取大约需要一秒钟,且爬虫会在等待响应后才继续处理下一个 URL。
- 链接提取逻辑不支持通过移除查询字符串参数来规范化 URL,无法处理相对锚点/片段 URL(如 href="#anchor"),也不支持按域名过滤 URL 或过滤对静态文件的请求。
在下一节中,我们将了解 Scrapy 如何解决这些问题,并如何轻松扩展网络爬虫的功能以满足自定义用例的需求。
如何使用 Scrapy 框架在 Python 中构建网络爬虫
Scrapy 是一个用于在 Python 中创建网络爬虫的强大框架。它提供了内置的链接跟踪和网页信息提取功能。您需要创建一个新的 Scrapy 项目和一个蜘蛛(spider),以定义爬虫的行为。
在开始爬取亚马逊等网站之前,务必检查该网站的 robots.txt 文件,以确认哪些 URL 路径被允许访问。当 ROBOTSTXT_OBEY 设置为 true 时(这是使用 Scrapy 命令 `startproject` 创建项目的默认值),Scrapy 会自动读取该文件并遵循其规则。
要创建一个新的 Scrapy 项目,您需要运行以下命令:
$ scrapy startproject amazon_crawler
该命令将生成一个具有以下结构的项目:
amazon_crawler/
├── scrapy.cfg
└── amazon_crawler
├── __init__.py
├── items.py
├── middlewares.py
├── pipelines.py
├── settings.py
└── spiders
├── __init__.py
要创建蜘蛛,请使用 Scrapy CLI 中的 `genspider` 命令。该命令的定义如下:
$ scrapy genspider [options] <name> <domain>
要为该爬虫生成蜘蛛,我们可以运行:
$ cd amazon_crawler
$ scrapy genspider baby_products amazon.com
这将在名为 `spiders` 的文件夹内生成一个名为 `baby_products.py` 的文件,并生成以下代码:
import scrapy
class BabyProductsSpider(scrapy.Spider):
name = 'wikipedia'
allowed_domains = ['en.wikipedia.com']
start_urls = ['http://en.wikipedia.com/']
def parse(self, response):
pass
Scrapy 还提供了多种预构建的蜘蛛类,例如 CrawlSpider、XMLFeedSpider、CSVFeedSpider 和 SitemapSpider。CrawlSpider 类基于基础 Spider 类构建,它包含一个额外的 "rules" 属性,用于定义如何在网站中导航。每条规则都会使用 LinkExtractor 来确定应从每个页面中提取哪些链接。
针对我们的用例,应让 Spider 类继承自 CrawlSpider。我们还需要创建一个 LinkExtractor 规则,指示爬虫仅从亚马逊的分页中提取链接。请记住,我们的目标是收集亚马逊上所有婴儿产品的数据,因此我们并不希望追踪页面上找到的所有链接。
接着,我们需要在类中再创建两个方法:`parse_item` 和 `parse_product`。`parse_item` 将作为回调函数传递给我们的 LinkExtractor 规则,并在提取每个链接时被调用。`parse_product` 将解析每个产品……¯\_(ツ)_/¯
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from bs4 import BeautifulSoup
class BabyProductsSpider(CrawlSpider):
name = 'baby_products'
allowed_domains = ['amazon.com']
start_urls = ['https://amazon.com/s?k=baby+products']
rules = (
Rule(
LinkExtractor(
restrict_css='.s-pagination-strip'
),
callback='parse_item',
follow=True),
)
def parse_item(self, response):
soup = BeautifulSoup(response.text, 'html.parser')
products = soup.select('div[data-component-type="s-search-result"]')
data = []
for product in products:
parsed_product = self.parse_product(product)
if (parsed_product != 'error'):
data.append(parsed_product)
return {
'url': response.url,
'data': data
}
def parse_product(self, product):
try:
link = product.select_one('a.a-text-normal')
price = product.select_one('span.a-price > span.a-offscreen').text
return {
'product_url': link['href'],
'name': link.text,
'price': price
}
except:
return 'error'
要启动爬虫,可以运行:
$ scrapy crawl baby_products
控制台将显示大量日志(可通过 `--logfile [log_file_name]` 指定日志文件)。
我以亚马逊搜索为例,演示了在 Python 中创建网络爬虫的基础知识。不过,该爬虫发现的可用链接不多,且并未针对特定数据用例进行优化。如果您希望从亚马逊搜索中提取特定数据,可以考虑使用我们的亚马逊商品数据 API。我们为亚马逊搜索、商品和分类页面创建了自定义解析器,它返回的 JSON 格式数据可直接用于您的应用程序。
为何使用专业的数据抓取服务比使用爬虫更好
虽然网络爬虫是提取互联网数据的有用工具,但其配置过程往往既耗时又复杂。此外,网络抓取可能违反某些网站的服务条款,导致您的 IP 地址被封禁,甚至面临法律诉讼。
另一方面,专业的数据抓取服务采用先进的技术手段,能够绕过反抓取措施,在不被察觉的情况下提取数据。它们还负责维护和扩展抓取基础设施,让您能够专注于数据的分析和利用。此外,由于能够处理更复杂的数据提取场景并胜任大规模抓取任务,这些服务还能提供更高水平的数据准确性和完整性。
总结
综上所述,虽然网络爬虫是提取互联网数据的有用工具,但其配置过程既耗时又复杂。此外,网络爬虫可能违反某些网站的服务条款,导致您的IP被封禁,甚至面临法律诉讼。因此,对于更复杂和大规模的爬取任务,最好使用专业的爬取服务。
如果您正在寻找替代自行爬网的方案,不妨考虑使用 WebScrapingAPI。WebScrapingAPI 是一项专业的网页抓取服务,让您无需构建和维护自己的网页抓取工具,即可轻松从网站中提取数据。
这是一款快速、可靠且经济高效的解决方案,适合各种规模的企业。




