网络爬虫简介(5)— 链接爬虫
优采云 发布时间: 2020-05-31 08:011.5.5 链接爬虫
到目前为止,我们早已借助示例网站的结构特性实现了两个简单爬虫,用于下载所有已发布的国家(或地区)页面。只要这两种技术可用,就应该使用它们进行爬取,因为这两种方式将须要下载的网页数目降至最低。不过,对于另一些网站爬虫社区,我们须要使爬虫表现得更象普通用户,跟踪链接,访问感兴趣的内容。
通过跟踪每位链接的方法,我们可以很容易地下载整个网站的页面。但是,这种方式可能会下载好多并不需要的网页。例如,我们想要从一个在线峰会中抓取用户帐号详情页,那么此时我们只须要下载帐号页,而不需要下载讨论贴的页面。本章使用的链接爬虫将使用正则表达式来确定应该下载什么页面。下面是这段代码的初始版本。
import re
def link_crawler(start_url, link_regex):
""" Crawl from the given start URL following links matched by
link_regex
"""
crawl_queue = [start_url]
while crawl_queue:
url = crawl_queue.pop()
html = download(url)
if html is not None:
continue
# filter for links matching our regular expression
for link in get_links(html):
if re.match(link_regex, link):
crawl_queue.append(link)
def get_links(html):
""" Return a list of links from html
"""
# a regular expression to extract all links from the webpage
webpage_regex = re.compile("""<a[^>]+href=["'](.*?)["']""",
re.IGNORECASE)
# list of all links from the webpage
return webpage_regex.findall(html)
要运行这段代码,只须要调用
link_crawler
函数,并传入两个参数:
要爬取的网站
URL
以及用于匹配你想跟踪的链
接的正则表达式。对于示例网
站来说,我们想要爬取的是国家(或地区
)列表索引页和国家(或地区)页面。
我们查看站点可以获知索引页链接遵守如下格式:
国家(或地区)页遵守如下格式:
因此爬虫社区,我们可以用/(index|view)/这个简单的正则表达式来匹配这两类网页。当爬虫使用这种输入参数运行时会发生哪些呢?你会得到如下所示的下载错误。
>>> link_crawler('http://example.python-scraping.com', '/(index|view)/')
Downloading: http://example.python-scraping.com
Downloading: /index/1
Traceback (most recent call last):
...
ValueError: unknown url type: /index/1
正则表达式是从字符串中抽取信息的非常好的工具,因此我推荐每名程序员都应该“学会怎样阅读和编撰一些正则表达式”。即便这么,它们常常会特别脆弱,容易失效。我们将在本书后续部份介绍更先进的抽取链接和辨识页面的形式。可以看出,问题出在下载/index/1时,该链接只有网页的路径部份,而没有合同和服务器部份,也就是说这是一个相对链接。由于浏览器晓得你正在浏览那个网页,并且还能采取必要步骤处理那些链接,因此在浏览器浏览时,相对链接是才能正常工作的。但是,urllib并没有上下文。为了使urllib才能定位网页,我们须要将链接转换为绝对链接的方式,以便包含定位网页的所有细节。如你所愿,Python的urllib中有一个模块可以拿来实现该功能,该模块名为parse。下面是link_crawler的改进版本,使用了urljoin方式来创建绝对路径。
from urllib.parse import urljoin
def link_crawler(start_url, link_regex):
""" Crawl from the given start URL following links matched by
link_regex
"""
crawl_queue = [start_url]
while crawl_queue:
url = crawl_queue.pop()
html = download(url)
if not html:
continue
for link in get_links(html):
if re.match(link_regex, link):
abs_link = urljoin(start_url, link)
crawl_queue.append(abs_link)
当你运行这段代码时,会听到似乎下载了匹配的网页,但是同样的地点总是会被不断下载到。产生该行为的诱因是那些地点互相之间存在链接。比如,澳大利亚链接到了南极洲,而南极洲又链接回了德国,此时爬虫都会继续将这种URL装入队列,永远不会抵达队列尾部
。要想避开重复爬取相同的链接,我们须要记录什么链接早已被爬取过。下面是更改后的link_crawler函数,具备了储存已发觉URL的功能,可以避免重复下载。
def link_crawler(start_url, link_regex):
crawl_queue = [start_url]
# keep track which URL's have seen before
seen = set(crawl_queue)
while crawl_queue:
url = crawl_queue.pop()
html = download(url)
if not html:
continue
for link in get_links(html):
# check if link matches expected regex
if re.match(link_regex, link):
abs_link = urljoin(start_url, link)
# check if have already seen this link
if abs_link not in seen:
seen.add(abs_link)
crawl_queue.append(abs_link)
当运行该脚本时,它会爬取所有地点,并且还能如期停止。最终,我们得到了一个可用的链接爬虫!