异步社区

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

34篇博客

网络爬虫简介(4)— 网站地图爬虫

异步社区2018-12-20 12:43


1.5.3 网站地图爬虫
在第一个简单的爬虫中,我们将使用示例网站robots.txt文件中发现的网站地图来下载所有网页。为了解析网站地图,我们将会使用一个简单的正则表达式,从<loc>标签中提取出URL。我们需要更新代码以处理编码转换,因为我们目前的download函数只是简单地返回了字节。而在下一章中,我们将会介绍一种更加稳健的解析方法——CSS选择器。下面是该示例爬虫的代码。
import re
def download(url, user_agent='wswp', num_retries=2, charset='utf-8'):
print('Downloading:', url)
request = urllib.request.Request(url)
request.add_header('User-agent', user_agent)
try:
resp = urllib.request.urlopen(request)
cs = resp.headers.get_content_charset()
if not cs:
cs = charset
html = resp.read().decode(cs)
except (URLError, HTTPError, ContentTooShortError) as e:
print('Download error:', e.reason)
html = None
if num_retries > 0:
if hasattr(e, 'code') and 500 <= e.code < 600:
# recursively retry 5xx HTTP errors
return download(url, num_retries - 1)
return html
def crawl_sitemap(url):
# download the sitemap file
sitemap = download(url)
# extract the sitemap links
links = re.findall('<loc>(.*?)</loc>', sitemap)
# download each link
for link in links:
html = download(link)
# scrape html here
# ...


现在,运行网站地图爬虫,从示例网站中下载所有国家或地区页面。>>>
crawl_sitemap('http://example.python-scraping.com/sitemap.xml')
Downloading: http://example.
python-scraping
.com/sitemap.xml
Downloading: http://example.
python-scraping
.com/view/Afghanistan-1
Downloading: http://example.
python-scraping
.com/view/Aland-Islands-2
Downloading: http://example.
python-scraping
.com/view/Albania-3
...


正如上面代码中的download方法所示,我们必须更新字符编码才能利用正则表达式处理网站响应。Python的read方法返回字节,而正则表达式期望的则是字符串。我们的代码依赖于网站维护者在响应头中包含适当的字符编码。如果没有返回字符编码头部,我们将会把它设置为默认值UTF-8,并抱有最大的希望。当然,如果返回头中的编码不正确,或是编码没有设置并且也不是UTF-8的话,则会抛出错误。还有一些更复杂的方式用于猜测编码(参见https://pypi.python.org/pypi/chardet),该方法非常容易实现。
到目前为止,网站地图爬虫已经符合预期。不过正如前文所述,我们无法依靠Sitemap文件提供每个网页的链接。下一节中,我们将会介绍另一个简单的爬虫,该爬虫不再依赖于Sitemap文件。
如果你在任何时候不想再继续爬取,可以按下Ctrl + C或cmd + C退出Python解释器或执行的程序。


1.5.4 ID 遍历爬虫
本节中,我们将利用网站结构的弱点,更加轻松地访问所有内容。下面是一些示例国家(或地区)的URL。http://example.pythonscraping.com/view/Afghanistan-1
http://example.python-scraping.com/view/Australia-2
http://example.python-scraping.com/view/Brazil-3
可以看出,这些URL只在URL路径的最后一部分有所区别,包括国家(或地区)名(作为页面别名)和ID。在URL中包含页面别名是非常普遍的做法,可以对搜索引擎优化起到帮助作用。一般情况下,We b服务器会忽略这个字符串,只使用ID来匹配数据库中的相关记录。下面我们将其移除,查看http://example.python-scraping.com/view/1,测试示例网站中的链接是否仍然可用。测试结果如图1.1所示。


从图1.1中可以看出,网页依然可以加载成功,也就是说该方法是有用的。现在,我们就可以忽略页面别名,只利用数据库ID来下载所有国家(或地区)的页面了。下面是使用了该技巧的代码片段。
import itertools
def crawl_site(url):
for page in itertools.count(1):
pg_url = '{}{}'.format(url, page)
html = download(pg_url)
if html is None:
break
# success - can scrape the result
现在,我们可以使用该函数传入基础URL。
>>> crawl_site('http://example.python-scraping.com/view/-')
Downloading: http://example.python-scraping.com/view/-1
Downloading: http://example.python-scraping.com/view/-2
Downloading: http://example.python-scraping.com/view/-3
Downloading: http://example.python-scraping.com/view/-4
[...]


在这段代码中,我们对ID进行遍历,直到出现下载错误时停止,我们假设此时抓取已到达最后一个国家(或地区)的页面。不过,这种实现方式存在一个缺陷,那就是某些记录可能已被删除,数据库ID之间并不是连续的。此时,只要访问到某个间隔点,爬虫就会立即退出。下面是这段代码的改进版本,在该版本中连续发生多次下载错误后才会退出程序。
def crawl_site(url, max_errors=5):
for page in itertools.count(1):
pg_url = '{}{}'.format(url, page)
html = download(pg_url)
if html is None:
num_errors += 1
if num_errors == max_errors:
# max errors reached, exit loop
break
else:
num_errors = 0
# success - can scrape the result


上面代码中实现的爬虫需要连续5次下载错误才会停止遍历,这样就很大程度上降低了遇到记录被删除或隐藏时过早停止遍历的风险。在爬取网站时,遍历ID是一个很便捷的方法,但是和网站地图爬虫一样,这种方法也无法保证始终可用。比如,一些网站会检查页面别名是否在
URL中,如果不是,则会返回404 Not Found错误。而另一些网站则会使用非连续大数作为ID,或是不使用数值作为ID,此时遍历就难以发挥其作用了。例如,Amazon使用ISBN作为可用图书的ID,这种编码包含至少10位数字。使用ID对ISBN进行遍历需要测试数十亿次可能的组合,因此这种方法肯定不是抓取该站内容最高效的方法。正如你一直关注的那样,你可能已经注意到一些TOO MANY REQUESTS下载错误信息。现在无须担心它,我们将会在1.5.5节的“高级功能”部分中介绍更多处理该类型错误的方法。


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