异步社区

异步社区是国内领先的IT专业图书社区,由人民邮电出版社主办,致力于优质学习内容的出版和分享。

34篇博客

网络爬虫简介(5)— 链接爬虫

异步社区2018-12-20 14:05

1.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
以及用于匹配你想跟踪的链
接的正则表达式。对于示例网
站来说,我们想要爬取的是国家(或地区
)列表索引页和国家(或地区)页面。
我们查看站点可以得知索引页链接遵循如下格式:
http://example.python-scraping.com/index/1
http://example.python-scraping.com/index/2国家(或地区)页遵循如下格式:
http://example.python-scraping.com/view/Afghanistan-1
http://example.python-scraping.com/view/Aland-Islands-2
因此,我们可以用/(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)


当运行该脚本时,它会爬取所有地点,并且能够如期停止。最终,我们得到了一个可用的链接爬虫!


原文网址:https://www.epubit.com/book/detail/33225
内容来源:异步社区;版权属【人民邮电出版社 异步社区】所有,转载已获得授权;未经授权,不得以任何方式复制和传播本书内容,如需转载请联系异步社区。