下载网页 (重传)
重传功能:如果返回如503等错误吗,可以尝试重传,错误吗可以参考:https://tools.ietf.org/html/rfc7231import urllib2
def download(url, num_retries=2):
print 'Downloading:',url
try:
html = urllib2.urlopen(url).read()
except urllib2.URLError as e:
print 'Download error:', e.reason
html = None
# 此部分实现当返回错误为5xx时,进行重传
if num_retries > 0:
if hasattr(e, 'code') and 500 <= e.code < 600:
return download(url, num_retries-1)
# ——重传功能结束
return html
# 测试:这个网站始终会返回 500 错误吗
download('http://httpstat.us/500')
运行结果:
设置用户代理
缺省情况下,urllib2使用 Python-urllib/2.7 作为用户代理下载网页内容,但有些网站会封禁这个用户代理,如 http://www.meetup.com/因此,我们需要控制用户代理的设定,例如下面设定默认的用户代理“wswp”。
def download_c(url, user_agent='wswp', num_retries=2):
print 'Downloading:',url
# 设置http报文头中的代理字段
headers = {'User-agent': user_agent}
request = urllib2.Request(url, headers=headers)
try:
html = urllib2.urlopen(request).read()
except urllib2.URLError as e:
print 'Download error:', e.reason
html = None
if num_retries > 0:
if hasattr(e, 'code') and 500 <= e.code < 600:
return download (url, user_agent, num_retries-1)
return html
download_c('http://www.meetup.com')
ID遍历爬虫
如下面这个网站URL “http://example.webscraping.com/view/Afghanistan-1” 一般情况下,Web服务器会忽略最后的这个 Afghanistan-1 字符串,只是用后面-1 的 1 这个ID来匹配对应页面,例如访问 http://example.webscraping.com/view/1 仍然可以访问同一个页面。因此在一些网站,我们可以忽略最后一个字段中的别名,只遍历ID来下载所有页面。
import itertools
max_errors = 5 # 设置最大错误数
num_errors = 0 # 初始错误数
# itertools.count(start, step) 无限迭代器 从start数值开始,默认为1,以step为步长无限迭代下去,直到break
for page in itertools.count(1):
url = 'http://example.webscraping.com/view/%d' % page
html = download(url)
if html is None:
# 错误次数加一
num_errors += 1
if num_errors == max_errors:
break
else:
num_errors = 0
链接爬虫
让爬虫跟踪页面中的链接,来访问对应页面。# 调用正则能模块 “re”
import re
# urlparse 该模块将“相对链接”转换为”绝对链接“
import urlparse
# seed_url 种子网址 link_regex:跟踪链接的正则能表达时
def link_crawler (seed_url, link_regex):
cral_queue = [seed_url] # 中括号代表由 seed_url 构成的 list 数据类型
# 跟踪之前访问过的网址 (keep track which URL's have s
een before)
seen = set(cral_queue) # 把访问链接 放入 seen 这个集合中 set()函数 添加到集合
while cral_queue:
url = cral_queue.pop() # pop()方法从列表移除并返回最后一个对象或obj。
html = download(url)
# 提取页面中的所有链接
for link in get_links(html):
# 检查是否匹配跟踪链接的正则能表达时
# re.match 从第一个字符开始匹配,如果不是起始位置匹配成功,match就返回none
if re.match(link_regex, link):
# urlparse.urljoin 把相对链接转换为绝对链接
# 例如 urljoin("http://www.google.com/1/a.html", "b.html") 得到http://www.google.com/1/b.html
link = urlparse.urljoin(seed_url, link)
# 检查之前是否访问过该链接 在set集合 seen 中查找
if link not in seen:
seen.add(link) # set.add 在集合中添加新的对象
# 在队列中列表list中加入这个链接
crawl_queue.append(link) # list.append 在列表末尾添加新的对象
# 返回 html 中的链接 list
def get_links(html):
# 一个正则能表达式来提取网页中的所有链接
# re.compile函数根据一个模式字符串的可选的标志参数生成一个正则能表达式对象,再通过findall返回所有匹配结果列表list。
webpage_regex = re.compile('<a[^>]+href=["\'](.*?)["\']', re.IGNORECASE)
# re.IGNORECASE 忽略大小写
#匹配例如 <a1 href= "link.html" 匹配链接的格式
# 列出 webpage 中所有的链接
return webpage_regex.findall(html) # findall 返回的是一个列表 里面是所有的链接
正则能表达式:
'<a[^>]+href=["\'](.*?)["\']'
[] 表示匹配其中任意字符
^> 表示除了 > 的字符
['\"] 表示 ‘ \ " 这三个字符中出现其中的任意一个
(.*?)中: . 可以匹配除 \n 外的任意字符
* 表示前面字符任意多个
? 匹配前面的子表达时0次或者1次
解析 robots.txt
robots.txt这个文件是让爬取者了解爬取该网站是存在的限制。 协议信息:http://www.robotstxt.orgPython自带的robotparser可以解析robot文件。
import robotparser
rp = robotparser.RobotFileParser()
rp.set_url('http://example.webscraping.com/robots.txt')
rp.read()
url = 'http://example.webscraping.com'
user_agent = 'BadCrawler'
rp.can_fetch(user_agent, url)
爬虫最大深度
深度——当前网页经过了多少个链接 当达到最大深度时,爬虫就不再向队列中添加该网页中的链接了。要实现这个功能,需要修改seen变量,该变量原先只记录访问过的网页链接,现在修改为一个字典,增加了页面深度的记录。
def link_crawler_d(...,depth=2):
max_depth = 2
seen = {}
...
depth = seen[url] #获取seen字典序中 url 所对应的深度值
if depth != max_depth:
for link in links:
if link not in seen:
seen[link] = depth+1
crawl_queue.append(link)