Python网络爬虫框架的总结(python中的爬虫框架)
off999 2024-09-21 20:52 17 浏览 0 评论
前言
虽然使用reqeuests和bs4可以处理网站数据获取但是当你要爬去大批量的网页时,单独的使用reqeuests就显得力不从心了。因为这是两个层面的事情,解决的问题是不一样的。
网络爬虫
网络爬虫是一种强大的技术,通过查找一个或多个域名的所有 URL 来从 Web 网站收集需要的数据。Python 有一些流行的网络爬虫库和框架。
介绍简单的例子,使用两个库在 Python 中从头开始构建一个简单的网络爬虫:Requests 和 Beautiful Soup。接着使用 Scrapy 构建一个示例爬虫,从 IMDb 收集电影元数据,并查看 Scrapy 如何扩展到拥有数百万页面的网站。
什么是网络爬虫?
网络爬虫和网络抓取是两个不同但相关的概念。网页爬虫是网页抓取的一个组成部分,爬虫逻辑查找要由抓取工具代码处理的 URL。
网络爬虫从要访问的 URL 列表开始,称为种子。对于每个 URL,爬网程序会在 HTML 中查找链接,根据某些条件过滤这些链接,并将新链接添加到队列中。所有 HTML 或某些特定信息都被提取出来,由不同的管道进行处理。
其实简单的殂就是我们打开淘宝首页,有很多个链接可以点击,打开每个链接又会进入不同的链接,这些子链接也有更多的链接。爬虫可以处理这个形成蜘蛛网一样的链接。
网络爬虫策略
网络爬虫只会访问一部分网页,具体取决于爬虫预算,该预算可以是每个域的最大网页数、深度或执行时间。
许多网站都提供了一个机器人.txt文件,以指示可以抓取网站的哪些路径,哪些是禁止的。还有sitemap.xml,它比robots.txt更明确一些,专门指示机器人应该抓取哪些路径,并为每个URL提供额外的元数据。
流行的网络爬虫用例包括:
- 搜索引擎(例如 Googlebot、Bingbot、Yandex Bot 等)收集了 Web 重要部分的所有 HTML。此数据已编制索引,使其可搜索。
- 除了收集 HTML 之外,SEO 分析工具还收集元数据,例如响应时间、检测损坏页面的响应状态以及收集反向链接的不同域之间的链接。
- 价格监控工具抓取电子商务网站以查找产品页面并提取元数据,尤其是价格。然后定期重新访问产品页面。
- Common Crawl 维护一个开放的 Web 爬虫数据存储库。例如,2022 年 5 月的档案包含 34.5 亿个网页。
从头开始在 Python 中构建一个简单的网络爬虫
要在 Python 中构建一个简单的网络爬虫,至少需要一个库来从 URL 下载 HTML,另一个库来提取链接。Python 提供了用于执行 HTTP 请求的标准库 urllib 和用于解析 HTML 的 html.parser。可以在 Github 上找到一个仅使用标准库构建的示例 Python 爬虫。
还有其他流行的库,例如 Requests 和 Beautiful Soup,它们可以在编写 HTTP 请求和处理 HTML 文档时提供改进的开发人员体验。如果您想了解更多信息,可以查看有关最佳 Python HTTP 客户端的指南。
本地安装这两个库。
pip install requests bs4
import logging
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
logging.basicConfig(
format='%(asctime)s %(levelname)s:%(message)s',
level=logging.INFO)
class Crawler:
def __init__(self, urls=[]):
self.visited_urls = []
self.urls_to_visit = urls
def download_url(self, url):
return requests.get(url).text
def get_linked_urls(self, url, html):
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)
yield path
def add_url_to_visit(self, url):
if url not in self.visited_urls and url not in self.urls_to_visit:
self.urls_to_visit.append(url)
def crawl(self, url):
html = self.download_url(url)
for url in self.get_linked_urls(url, html):
self.add_url_to_visit(url)
def run(self):
while self.urls_to_visit:
url = self.urls_to_visit.pop(0)
logging.info(f'Crawling: {url}')
try:
self.crawl(url)
except Exception:
logging.exception(f'Failed to crawl: {url}')
finally:
self.visited_urls.append(url)
if __name__ == '__main__':
Crawler(urls=['https://www.imdb.com/']).run()
(使用 Requests 库、使用 Beautiful Soup 库和过滤 URL),然后继续使用我们的 IMDb 起始 URL 实例化该类并调用其方法。Crawlerdownload_urlget_linked_urlsadd_url_to_visitrun()
只要 中存在待处理的 URL,就会运行,会将每个 URL 传递给 ,提取任何链接,并将它们添加到 - 冲洗并重复。runurls_to_visitcrawl()urls_to_visit
要运行我们的爬虫,只需在命令行中输入此命令即可。
python crawler.py
爬网程序为每个访问过的 URL 记录一行。
INFO:Crawling: https://www.imdb.com/
INFO:Crawling: https://www.imdb.com/?ref_=nv_home
INFO:Crawling: https://www.imdb.com/calendar/?ref_=nv_mv_cal
INFO:Crawling: https://www.imdb.com/list/ls016522954/?ref_=nv_tvv_dvd
INFO:Crawling: https://www.imdb.com/chart/top/?ref_=nv_mv_250
INFO:Crawling: https://www.imdb.com/chart/moviemeter/?ref_=nv_mv_mpm
INFO:Crawling: https://www.imdb.com/feature/genre/?ref_=nv_ch_gr
代码非常简单,但在成功抓取完整网站之前,有许多性能和可用性问题需要解决。
- 爬网程序速度较慢,不支持并行性。从时间戳可以看出,抓取每个 URL 大约需要一秒钟。每次爬网程序发出请求时,它都会等待响应,不会执行太多其他操作。
- 下载 URL 逻辑没有重试机制,URL 队列不是真正的队列,并且对于大量 URL 效率不高。
- 链接提取逻辑不支持通过删除 URL 查询字符串参数来标准化 URL,不处理相对锚点/片段 URL(即 ),也不支持按域过滤 URL 或过滤掉对静态文件的请求。href="#myanchor"
- 抓取工具不会识别自己,并忽略漫游器 .txt 文件。
使用 Scrapy 进行网络爬虫
Scrapy 是最受欢迎的网络抓取和抓取 Python 框架,。
Scrapy 具有多组件架构。通常实现至少两个不同的类:Spider 和 Pipeline。Web 抓取可以被认为是一种 ETL,在其中从 Web 中提取数据并将其加载到存储中。蜘蛛提取数据,管道将其加载到存储中。转换可以发生在爬虫和管道中,
可以在组件之间添加爬虫和下载器中间件,如下图所示。
Scrapy 架构概述 [源代码]
from scrapy.spiders import Spider
class ImdbSpider(Spider):
name = 'imdb'
allowed_domains = ['www.imdb.com']
start_urls = ['https://www.imdb.com/']
def parse(self, response):
pass
Scrapy 还提供了几个通用的爬虫类:CrawlSpider、XMLFeedSpider、CSVFeedSpider 和 SitemapSpider。CrawlSpider 类继承自 Spider 基类,并提供额外的 rules 属性来定义如何抓取网站。每个规则都使用 LinkExtractor 来指定从每个页面中提取哪些链接。接下来,我们将通过为 IMDb(互联网电影数据库)构建爬虫来了解如何使用它们中的每一个。
为 IMDb 构建一个示例 Scrapy 爬虫
在尝试抓取 IMDb 之前,检查了 IMDb 机器人 .txt 文件,看看允许哪些 URL 路径。robots 文件仅禁止所有用户代理的 26 个路径。Scrapy 会事先读取 robot.txt 文件,并在 ROBOTSTXT_OBEY 设置为 时遵守该文件。使用 Scrapy 命令生成的所有项目都是这种情况。truestartproject
scrapy startproject scrapy_crawler
此命令使用默认的 Scrapy 项目文件夹结构创建一个新项目。
scrapy_crawler/
├── scrapy.cfg
└── scrapy_crawler
├── __init__.py
├── items.py
├── middlewares.py
├── pipelines.py
├── settings.py
└── spiders
├── __init__.py
然后可以使用规则创建一个蜘蛛来提取所有链接。scrapy_crawler/spiders/imdb.py
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class ImdbCrawler(CrawlSpider):
name = 'imdb'
allowed_domains = ['www.imdb.com']
start_urls = ['https://www.imdb.com/']
rules = (Rule(LinkExtractor()),)
现在,只需使用命令启动爬虫scrapy
scrapy crawl imdb --logfile imdb.log
将获得大量日志,包括每个请求的一个日志。在浏览日志时,我注意到,即使我们设置为仅抓取 https://www.imdb.com 下的网页,也会有对外部域(例如 amazon.com)的请求。allowed_domains
[scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET https://www.amazon.com/b/?&node=5160028011&ref_=ft_iba> from <GET [https://www.imdb.com/whitelist-offsite?url=https%3A%2F%2Fwww.amazon.com%2Fb%2F%3F%26node%3D5160028011%26ref_%3Dft_iba&page-action=ft-iba&ref=ft_iba](https://www.imdb.com/whitelist-offsite?url=https%3A%2F%2Fwww.amazon.com%2Fb%2F%3F%26node%3D5160028011%26ref_%3Dft_iba&page-action=ft-iba&ref=ft_iba)>
IMDb 将外部域下的路径重定向到外部域。有一个开放的 Scrapy Github 问题,该问题显示外部 URL 在应用之前不会被过滤掉。为了解决这个问题,可以将链接提取器配置为跳过以两个正则表达式开头的 URL。/whitelist-offsite/whitelistOffsiteMiddlewareRedirectMiddleware
class ImdbCrawler(CrawlSpider):
name = 'imdb'
allowed_domains = ['www.imdb.com']
start_urls = ['https://www.imdb.com/']
rules = (
Rule(LinkExtractor(
deny=[
re.escape('https://www.imdb.com/offsite'),
re.escape('https://www.imdb.com/whitelist-offsite'),
],
)),
)
Rule类支持多个参数来过滤 URL。例如可以忽略特定的文件扩展名,并通过对查询字符串进行排序或折叠来减少重复 URL 的数量。LinkExtractor
如果找不到用例的特定参数,则可以使用 LinkExtractor 或 Rule 的参数。例如两次获得相同的页面,一次是纯 URL,另一次是附加查询字符串参数。process_valueprocess_links
- https://www.imdb.com/name/nm1156914/
- https://www.imdb.com/name/nm1156914/?mode=desktop&ref_=m_ft_dsk
为了限制抓取的 URL 的数量,可以使用 w3lib 库中的 url_query_cleaner 函数从 URL 中删除所有查询字符串,并在 .process_links
from w3lib.url import url_query_cleaner
def process_links(links):
for link in links:
link.url = url_query_cleaner(link.url)
yield link
class ImdbCrawler(CrawlSpider):
name = 'imdb'
allowed_domains = ['www.imdb.com']
start_urls = ['https://www.imdb.com/']
rules = (
Rule(LinkExtractor(
deny=[
re.escape('https://www.imdb.com/offsite'),
re.escape('https://www.imdb.com/whitelist-offsite'),
],
), process_links=process_links),
)
现在已经限制了要处理的请求数,可以添加一种方法来从每个页面中提取数据并将其传递到管道进行存储。例如可以在不同的管道中对其进行处理,也可以选择 HTML 元数据。parse_itemresponse.text
要在标头标签中选择 HTML 元数据,可以指定自己的 XPath 表达式,最好使用库 extract 从 HTML 页面中提取所有元数据。
可以使用 .pip install extruct
import re
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from w3lib.url import url_query_cleaner
import extruct
def process_links(links):
for link in links:
link.url = url_query_cleaner(link.url)
yield link
class ImdbCrawler(CrawlSpider):
name = 'imdb'
allowed_domains = ['www.imdb.com']
start_urls = ['https://www.imdb.com/']
rules = (
Rule(
LinkExtractor(
deny=[
re.escape('https://www.imdb.com/offsite'),
re.escape('https://www.imdb.com/whitelist-offsite'),
],
),
process_links=process_links,
callback='parse_item',
follow=True
),
)
def parse_item(self, response):
return {
'url': response.url,
'metadata': extruct.extract(
response.text,
response.url,
syntaxes=['opengraph', 'json-ld']
),
}
该属性设置为,以便 Scrapy 仍然遵循每个响应的所有链接,配置了 extruct 以仅提取 Open Graph 元数据和 JSON-LD,这是一种在 Web 中使用 JSON 对链接数据进行编码的流行方法,由 IMDb 使用。可以运行爬网程序并将项目以 JSON 行格式存储到文件中。followTrue
scrapy crawl imdb --logfile imdb.log -o imdb.jl -t jsonlines
输出文件包含每个已爬网项的一行。例如,对于从 HTML 中的标记中获取的电影,提取的 Open Graph 元数据如下所示。imdb.jl<meta>
{
"url": "http://www.imdb.com/title/tt2442560/",
"metadata": {"opengraph": [{
"namespace": {"og": "http://ogp.me/ns#"},
"properties": [
["og:url", "http://www.imdb.com/title/tt2442560/"],
["og:image", "https://m.media-amazon.com/images/M/MV5BMTkzNjEzMDEzMF5BMl5BanBnXkFtZTgwMDI0MjE4MjE@._V1_UY1200_CR90,0,630,1200_AL_.jpg"],
["og:type", "video.tv_show"],
["og:title", "Peaky Blinders (TV Series 2013\u2013 ) - IMDb"],
["og:site_name", "IMDb"],
["og:description", "Created by Steven Knight. With Cillian Murphy, Paul Anderson, Helen McCrory, Sophie Rundle. A gangster family epic set in 1900s England, centering on a gang who sew razor blades in the peaks of their caps, and their fierce boss Tommy Shelby."]
]
}]}
}
单个项目的 JSON-LD 太长,无法包含在文章中,以下是 Scrapy 从标签中提取的内容的示例。<script type="application/ld+json">
"json-ld": [
{
"@context": "http://schema.org",
"@type": "TVSeries",
"url": "/title/tt2442560/",
"name": "Peaky Blinders",
"image": "https://m.media-amazon.com/images/M/MV5BMTkzNjEzMDEzMF5BMl5BanBnXkFtZTgwMDI0MjE4MjE@._V1_.jpg",
"genre": ["Crime","Drama"],
"contentRating": "TV-MA",
"actor": [
{
"@type": "Person",
"url": "/name/nm0614165/",
"name": "Cillian Murphy"
},
...
]
...
}
]
通过按顺序点击过滤器,爬虫会生成内容相同的网址,只是过滤器的应用顺序不同。
- https://www.imdb.com/name/nm2900465/videogallery/ content_type-拖车/related_titles-TT0479468
- https://www.imdb.com/name/nm2900465/videogallery/ related_titles-TT0479468/content_type-拖车
长过滤器和搜索 URL 是一个难题,可以通过使用 Scrapy 设置限制 URL 的长度来部分解决,URLLENGTH_LIMIT。
以 IMDb 为例来展示在 Python 中构建网络爬虫的基础知识。如果需要来自 IMDb 的特定数据,可以查看提供每日 IMDb 数据导出的 IMDb 数据集项目或 Cinemagoer,
大规模 Web 爬虫
如果尝试抓取像 IMDb 这样拥有超过 1.3 亿个页面的大型网站(至少根据 Google 的数据),请务必通过调整您的爬虫并相应地调整其设置来负责任地抓取。
- USER_AGENT - 允许您指定用户代理并提供可能的联系方式
- DOWNLOAD_DELAY - 指定爬网程序在请求之间应等待的秒数
- CONCURRENT_REQUESTS_PER_DOMAIN - 指示爬网程序应向一个站点发送的最大并发请求数
- AUTOTHROTTLE_ENABLED - 启用自动和动态请求限制
请注意,默认情况下,Scrapy 爬网针对单个域进行了优化。如果要对多个网域进行爬网,请检查这些设置以针对广泛爬网进行优化,包括将默认爬网顺序从深度优先更改为呼吸优先。要限制抓取预算,可以使用关闭爬虫扩展程序的CLOSESPIDER_PAGECOUNT设置来限制请求数。
使用默认设置,Scrapy 每分钟为 IMDb 等网站抓取约 600 个页面。以这种速度,用一个机器人抓取 1.3 亿个页面大约需要半年时间。如果您需要抓取多个网站,最好为每个大型网站或网站组启动单独的爬虫。如果您对分布式 Web 爬网感兴趣,可以阅读开发人员如何使用 20 个 Amazon EC2 机器实例在不到两天的时间内使用 Python 抓取 250M 个页面。
在某些情况下,可能会遇到需要您执行 JavaScript 代码来呈现所有 HTML 的网站。否则,可能无法收集网站上的所有链接。因为现在网站在浏览器中动态渲染内容非常普遍,所以我编写了一个 Scrapy 中间件,用于使用 ScrapingBee 的 API 渲染 JavaScript 页面。
这一点主要是解决这种使用JavaScript动态生成HTML页面的情况。
结论
- HTML是传统爬虫的关键
- 新的WEB开发技术使用JavaScript来生成页面,实际爬虫任务比这个复杂
- requests bs4 是基础的库,
- scrapy是一个爬虫框架。
相关推荐
- python入门到脱坑经典案例—清空列表
-
在Python中,清空列表是一个基础但重要的操作。clear()方法是最直接的方式,但还有其他方法也可以实现相同效果。以下是详细说明:1.使用clear()方法(Python3.3+推荐)...
- python中元组,列表,字典,集合删除项目方式的归纳
-
九三,君子终日乾乾,夕惕若,厉无咎。在使用python过程中会经常遇到这四种集合数据类型,今天就对这四种集合数据类型中删除项目的操作做个总结性的归纳。列表(List)是一种有序和可更改的集合。允许重复...
- Linux 下海量文件删除方法效率对比,最慢的竟然是 rm
-
Linux下海量文件删除方法效率对比,本次参赛选手一共6位,分别是:rm、find、findwithdelete、rsync、Python、Perl.首先建立50万个文件$testfor...
- 数据结构与算法——链式存储(链表)的插入及删除,
-
持续分享嵌入式技术,操作系统,算法,c语言/python等,欢迎小友关注支持上篇文章我们讲述了链表的基本概念及一些查找遍历的方法,本篇我们主要将一下链表的插入删除操作,以及采用堆栈方式如何创建链表。链...
- Python自动化:openpyxl写入数据,插入删除行列等基础操作
-
importopenpyxlwb=openpyxl.load_workbook("example1.xlsx")sh=wb['Sheet1']写入数据#...
- 在Linux下软件的安装与卸载(linux里的程序的安装与卸载命令)
-
通过apt安装/协助软件apt是AdvancedPackagingTool,是Linux下的一款安装包管理工具可以在终端中方便的安装/卸载/更新软件包命令使用格式:安装软件:sudoapt...
- Python 批量卸载关联包 pip-autoremove
-
pip工具在安装扩展包的时候会自动安装依赖的关联包,但是卸载时只删除单个包,无法卸载关联的包。pip-autoremove就是为了解决卸载关联包的问题。安装方法通过下面的命令安装:pipinsta...
- 用Python在Word文档中插入和删除文本框
-
在当今自动化办公需求日益增长的背景下,通过编程手段动态管理Word文档中的文本框元素已成为提升工作效率的关键技术路径。文本框作为文档排版中灵活的内容容器,既能承载多模态信息(如文字、图像),又可实现独...
- Python 从列表中删除值的多种实用方法详解
-
#Python从列表中删除值的多种实用方法详解在Python编程中,列表(List)是一种常用的数据结构,具有动态可变的特性。当我们需要从列表中删除元素时,根据不同的场景(如按值删除、按索引删除、...
- Python 中的前缀删除操作全指南(python删除前导0)
-
1.字符串前缀删除1.1使用内置方法Python提供了几种内置方法来处理字符串前缀的删除:#1.使用removeprefix()方法(Python3.9+)text="...
- 每天学点Python知识:如何删除空白
-
在Python中,删除空白可以分为几种不同的情况,常见的是针对字符串或列表中空白字符的处理。一、删除字符串中的空白1.删除字符串两端的空白(空格、\t、\n等)使用.strip()方法:s...
- Linux系统自带Python2&yum的卸载及重装
-
写在前面事情的起因是我昨天在测试Linux安装Python3的shell脚本时,需要卸载Python3重新安装一遍。但是通过如下命令卸载python3时,少写了个3,不小心将系统自带的python2也...
- 如何使用Python将多个excel文件数据快速汇总?
-
在数据分析和处理的过程中,Excel文件是我们经常会遇到的数据格式之一。本文将通过一个具体的示例,展示如何使用Python和Pandas库来读取、合并和处理多个Excel文件的数据,并最终生成一个包含...
- 【第三弹】用Python实现Excel的vlookup功能
-
今天继续用pandas实现Excel的vlookup功能,假设我们的2个表长成这样:我们希望把Sheet2的部门匹在Sheet1的最后一列。话不多说,先上代码:importpandasaspd...
- python中pandas读取excel单列及连续多列数据
-
案例:想获取test.xls中C列、H列以后(当H列后列数未知时)的所有数据。importpandasaspdfile_name=r'D:\test.xls'#表格绝对...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python自定义函数 (53)
- python进度条 (67)
- python吧 (67)
- python字典遍历 (54)
- python的for循环 (65)
- python格式化字符串 (61)
- python串口编程 (60)
- python读取文件夹下所有文件 (59)
- java调用python脚本 (56)
- python操作mysql数据库 (66)
- python字典增加键值对 (53)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python人脸识别 (54)
- python多态 (60)
- python命令行参数 (53)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)