对目标站点的规模和结构进行一定程度的了解
1.检查robots.txt:可以让爬虫了解爬取该网站时存在哪些限制;
2.检查网站地图:网站提供Sitemap文件可以帮助爬虫定位网站最新的内容,而无需爬取每一个网页;
3.估计网站大小;可以通过Google搜索的site关键词过滤域名结果;
4.网站所用技术:构件网站所用的技术也会对我们的爬虫产生影响,可以使用builtwith这个工具,它可以检查网站构件的技术类型。
安装工具
这里写代码片
pip install builtwith
以URL作为参数,下载该URL并对其进行分析,返回该网站所用的技术
>>> import builtwith
>>> builtwith.parse('http://example.webscraping.com')
{'web-servers': ['Nginx'], 'web-frameworks': ['Web2py', 'Twitter Bootstrap'], 'programming-languages': ['Python'], 'javascript-frameworks': ['jQuery', 'Modernizr', 'jQuery UI']}
可以看出示例网站使用了Python的Web2py框架,还是用了Twitter Bootstrap框架等信息。
5.寻找网站所有者
我们可以使用WHOIS协议查询域名的注册者是谁。Python中有一个针对该协议的封装库。
安装
pip install python-whois
返回结果
>>> import whois
>>> print(whois.whois('appspot.com'))
{
"domain_name": [
"APPSPOT.COM",
"appspot.com"
],
"registrar": "MarkMonitor, Inc.",
"whois_server": "whois.markmonitor.com",
"referral_url": null,
"updated_date": [
"2018-02-06 10:30:28",
"2018-02-06 02:30:29-08:00"
],
"creation_date": [
"2005-03-10 02:27:55",
"2005-03-09 18:27:55-08:00"
],
"expiration_date": [
"2019-03-10 01:27:55",
"2019-03-09 00:00:00-08:00"
],
"name_servers": [
"NS1.GOOGLE.COM",
"NS2.GOOGLE.COM",
"NS3.GOOGLE.COM",
"NS4.GOOGLE.COM",
"ns3.google.com",
"ns2.google.com",
"ns1.google.com",
"ns4.google.com"
],
"status": [
"clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited",
"clientTransferProhibited https://icann.org/epp#clientTransferProhibited",
"clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited",
"serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited",
"serverTransferProhibited https://icann.org/epp#serverTransferProhibited",
"serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited",
"clientUpdateProhibited (https://www.icann.org/epp#clientUpdateProhibited)",
"clientTransferProhibited (https://www.icann.org/epp#clientTransferProhibited)",
"clientDeleteProhibited (https://www.icann.org/epp#clientDeleteProhibited)",
"serverUpdateProhibited (https://www.icann.org/epp#serverUpdateProhibited)",
"serverTransferProhibited (https://www.icann.org/epp#serverTransferProhibited)",
"serverDeleteProhibited (https://www.icann.org/epp#serverDeleteProhibited)"
],
"emails": [
"abusecomplaints@markmonitor.com",
"whoisrelay@markmonitor.com"
],
"dnssec": "unsigned",
"name": null,
"org": "Google LLC",
"address": null,
"city": null,
"state": "CA",
"zipcode": null,
"country": "US"
}
编写第一个爬虫
常见的三种方法:
- 爬取网站地图;
- 便利每个网页数据库ID;
- 跟踪网页连接
重试下载
import urllib2
#使用递归,如果出错最多访问两次,如果没有访问到就返回None;否则返回页面内容
def download(url,num_retries=2):
print 'Downloading:',url
try:
html=urllib2.urlopen(url).read()
except urllib2.URLError as e:
print 'Download:' ,e.reason
html=None
if num_retries>0:
if hasattr(e,'code') and 500 <=e.code<600:
return download(url,num_retries-1)
return html
download('http://httpstat.us/500')
设置用户代理
默认情况下,urllib2使用Python-urllib/2.7作为用户代理下载页面内容,我嫩可以使用可辨识的用户代理‘wswp’;
def download(url,user_agent='wswp',num_retries=2):
print 'Downloading:',url
headers={'User-agent':user_agent}
request=urllib2.Request(url,headers=headers)
try:
html=urllib2.urlopen(request).read()
except urllib2.URLError as e:
print 'Download:' ,e.reason
html=None
if num_retries>0:
if hasattr(e,'code') and 500 <=e.code<600:
return download(url,num_retries-1)
return html
该函数能够捕获异常、重试下载并设置代理用户
网站地图爬虫
def crawl_sitemap(url):
sitemap=download(url)
links=re.findall('<loc>(.*?)</loc>',sitemap)
for link in links:
html=download(link)
crawl_sitemap('http://example.webscraping.com/sitemap.xml')
ID遍历爬虫
我们将利用网站结构的弱点 , 更加轻松地访问有内容。
- http:IIexample.webscraping.com/view/Afghanistan- 1
- http:IIexample.webscraping.com/view/Australia-2
- http:IIexample.webscraping.com/view/Brazil-3
这些URL只在结尾处有区别,包括国家名和ID,在URL中包含别名的做法是非常普遍的,可以对搜索引擎优化起到帮助作用。一般情况下,Web服务器会忽略这个字符串,只使用ID来匹配数据库的相关记录。
import itertools
max_errors=5
num_errors=0
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
五次重试下载后退出
链接爬虫
通过跟踪所有链接的方式,我们可以很容易地下载整个网站 的页面。但是,这种方法会下载大量我们并不需要的网页。例如, 我们想要从一个在线论坛中抓取用户账号详情页, 那么此时我们只需要下载 账号页, 而不需要下载讨论贴的页面。
#可以传入一个URL和指定的正则模式,只匹配需要的网站
def link_crawler(seed_url,link_regex):
crawl_queue=[seed_url]
#创建集合,用来存放已经访问过的链接
seen=set(crawl_queue)
while crawl_queue:
url=crawl_queue.pop()
html=download(url)
for link in get_links(html):
if re.search(link_regex,link):
#创建绝对链接的形式,urllib 2无法获知上下文,所以相对链接无法工作
link=urlparse.urljoin(seed_url,link)
#避免有的两个链接相互链接使爬虫循环访问
#创建集合seen,如果已经访问的就不再加入
if link not in seen:
# seen[link]=depth+1
crawl_queue.append(link)
seen.add(link)
def get_links(html):
webpage_regx=re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE)
return webpage_regx.findall(html)
高级功能
解析robots.txt
我们需要解析 robots. t xt 文件,以避免下载禁止 爬取的URL。使用Python自带的 robotp arser 模块, 就可以轻松完成这项工作。
import robotparser
#robotparser模块首先加载robots.txt文件
rp=robotparser.RobotFileParser()
rp.set_url('http://example.webscraping.com/robots.txt')
rp.read()
url='http://example.webscraping.com'
user_agent='BadCrawler'
#can_fetch确定指定的用户代理是否允许访问网页
rp.can_fetch(user_agent,url)
支持代理
有时我们需要使用代理访问某个网站 。比如,Net flix 屏蔽美国以外的大多数国家 。使用urlli b2支持代理并没 有想象中 那么容易( 可以尝试使用更友好的 Python HTTP模块re que sts来实现该功能。
def download(url,user_agent='wswp',proxy=None,num_retries=2):
print 'Downloading:',url
headers={'User-agent':user_agent}
request=urllib2.Request(url,headers=headers)
opener=urllib2.build_opener()
if opener:
proxy_params={urlparse.urlparse(url).scheme:proxy}
opener.add_handler(urllib2.ProxyHandler(proxy_params))
try:
html=urllib2.urlopen(request).read()
except urllib2.URLError as e:
print 'Download:' ,e.reason
html=None
if num_retries>0:
if hasattr(e,'code') and 500 <=e.code<600:
return download(url,num_retries-1)
return html
下载限速
如果我们爬取网站的速度过快,就会 面临被 封 禁或是造成服务器过载的风
险。为了降低这些风险, 我们可以在两次下载之间添加延 时, 从而对爬虫限
速。
Throttle类记录了每个域名上次访问的时间,如果当前时间距离上次
访问的时间小于延迟,则执行睡眠操作。
class Throttle:
def __init__(self,delay):
self.delay=delay
self.domains={}
def wait(self ,url):
#urlparse.urlparse将url地址拆成六个部分,netloc为域名服务器,也可能包含用户信息
domain=urlparse.urlparse(url).netloc
last_accessed=self.domains.get(domain)
if self.delay >0 and last_accessed is not None:
sleep_secs=self.delay-(datetime.datetime.now()-last_accessed).seconds
if sleep_secs>0:
time.sleep(sleep_secs)
#更新上一次访问时间
self.domains[domain]=datetime.datetime.now()
我们 可以在每 次下载之前调用Throttle对爬虫进行限速。
避免爬虫陷阱
我们的爬虫会跟踪所有之前没有访问过的链接 。但是, 一些网站会动态生成页面内容, 这样就会出现无限多的网 页 。 比如, 网 站 有一个在线日历功能, 提供了可以 访问 下个月和下 一年的链接, 那么下个月的页面中 同样会包含访问 再下个月的 链接, 这样页面就会无止境地 链接 下去。 这 种情况被
称为爬虫陷阱。
想要避免陷入爬虫 陷阱,一个简单的方 法是记录到达当前网页经过了多少个 链接, 也就是 深度。 当到达最大深度时, 爬虫就 不再向队列中添加该网页
中的链接了。