成功的秘诀,在永不改变既定的目的。 [ —卢梭 ]
特别声明:这个系列的爬虫相关知识总结,是根据小象学院(分布式爬虫课程)梳理而来。一方面作为自己学习过程的笔记,另一方面给想入门的同学提供参考。
本篇分为以下几部分:
该部分比较简单(略)。
网上已经有很多教程了,可以手动安装,用pip安装各种依赖包,也可以安装类似于Anaconda 的集成环境。需要指出的是该系列课程是基于python2的。
http是一个请求<->响应模式的典型范例。即客户端向服务器发送一个请求信息,服务器来响应这个信息。在老版本的http中,每个请求都将被创建一个新的连接,在此基础上发送接收请求,其优点是简单、易于理解与实现。但效率很低。
keep-alive功能使得客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,可避免建立重新连接。
默认情况下,http1.1中的所有连接都被保持,除非在请求头或响应头中指明关闭:Connection:Close
首先从根节点开始抓取,分析根节点超链接放入队列(要求无重复),依次访问队列中的链接,根据策略不同可分为广度优先与深度优先。
从A到D依次抓取,然后回到C,横向抓取到G,再回到B再横向抓取。
Bit-Map方法。建立一个BitSet,将每个URL经过一个哈希函数映射到某一位
实际操作中需要:
评估网站的网页数量(如site:www.taobao.com)
可遵循的原则:
该示例抓取马蜂窝网站上的各个城市游记数据(该网站结构清晰,比较典型)。
import urllib2
import httplib
import re
from pybloomfilter import BloomFilter
import os
request_headers = {
'host': "www.mafengwo.cn",
'connection': "keep-alive",
'cache-control': "no-cache",
'upgrade-insecure-requests': "1",
'user-agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36",
'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
'accept-language': "zh-CN,en-US;q=0.8,en;q=0.6"
}
#定义保存游记的目录
dirname = 'mafengwo_city_notes/'
#城市代码
city_code = []
这里采用pybloomfilter
download_bf = BloomFilter(1024*1024*16,0.01)
#实际大小需具体分析预估所抓取网页数量
打开www.mafengwo.com会发现所有的游记都是http://www.mafengwo.cn/yj/i/1-0-j.html的格式i是城市代码j是页面序号。因此抓取的思路就是先遍历城市代码,在每个城市下遍历游记页码,在该页码下抓取游记,游记页面格式为www.mafengwo.cn/i/后面跟7位数字。如http://www.mafengwo.cn/i/6536459.html
下载页面游记代码如下:
def download_city_notes(citycode):
#页面最大值设为999,应该没那么多页,但应大于最大页码,下面会有退出条件
for i in range(1,999):
url = "http://www.mafengwo.cn/yj/%s/1-0-%d.html" % (citycode,i)
#判断是否已经抓取,如果是开始下一页
if url in download_bf:
continue
pass
print 'start open url %s' % (url)
#即将抓取的放进download_bf
download_bf.add(url)
#发起请求
req_yj = urllib2.Request(url,headers= request_headers)
#接收响应
response_yj = urllib2.urlopen(req_yj)
#读取内容
htmlcontent_yj = response_yj.read()
#按游记格式匹配出html
city_notes_url = re.findall('href="/i/\d{7}.html',htmlcontent_yj)
# 这里就要判断该页面下游记是否抓完,抓完就返回,进入下一个城市
if len(city_notes_url) == 0:
return
#下面开始处理每篇游记,因为city_notes_url是一个页面下的所有游记
for city_note_url in city_notes_url:
try:
#游记url
notes_url = 'http://www.mafengwo.cn%s'%(city_note_url[6:])
#为避免重复抓取,依然要判断是否已抓
if notes_url in download_bf:
continue
pass
print 'download %s'%(notes_url)
req_note = urllib2.Request(notes_url,headers=request_headers)
response_note = urllib2.urlopen(req_note)
htmlcontent_note = response_note.read()
#定义游记名
filename = city_note_url[7:].replace('/','_')
#打开文件,写入
fo = open("%s%s"%(dirname,filename), 'wb+')
fo.write(htmlcontent_note)
#及时关闭
fo.close()
download_bf.add(notes_url)
except Exception,Arguments:
print Arguments
continue
因为拿到城市代码,就可以遍历该城市下的游记了,执行入口在此:
if __name__ == '__main__':
#判断目录是否存在,不存在则创建
if not os.path.exists(dirname):
os.makedirs(dirname)
pass
try:
#获取城市代码,在目的地页面下,格式为“www.mafengwo.cn/mdd/”
req = urllib2.Request("http://www.mafengwo.cn/mdd/",headers=request_headers)
response = urllib2.urlopen(req)
htmlcontent = response.read()
#城市主页
city_home_pages = re.findall('/travel-scenic-spot/mafengwo/\d{5}.html',htmlcontent)
for city in city_home_pages:
#截取城市代码
city_code.append(city[29:34])
#开始下载该城市下的游记了
download_city_notes(city[29:34])
except urllib2.HTTPError,Arguments:
print Arguments
except httplib.BadStatusLine:
print 'BadStatusLine'
except Exception,Arguments:
print Arguments
嗯 下面给出完整代码:
#-*- coding: utf-8 -*-
'''
Created on 31.3, 2017
pachong
Input: url
Output: data
@author: xl
'''
import urllib2
import httplib
import re
from pybloomfilter import BloomFilter
import os
from lxml import html
#print 'ww'
request_headers = {
'host': "www.mafengwo.cn",
'connection': "keep-alive",
'cache-control': "no-cache",
'upgrade-insecure-requests': "1",
'user-agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36",
'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
'accept-language': "zh-CN,en-US;q=0.8,en;q=0.6"
}
dirname = 'mafengwo_city_notes/'
city_code = []
#download_bf =[]
#defin bloomfilter
download_bf = BloomFilter(1024*1024*16,0.01)
def download_city_notes(citycode):
for i in range(1,999):
url = "http://www.mafengwo.cn/yj/%s/1-0-%d.html" % (citycode,i)
#if already get
if url in download_bf:
continue
pass
print 'start open url %s' % (url)
download_bf.add(url)
req_yj = urllib2.Request(url,headers= request_headers)
#print 111
response_yj = urllib2.urlopen(req_yj)
#print 222
htmlcontent_yj = response_yj.read()
city_notes_url = re.findall('href="/i/\d{7}.html',htmlcontent_yj)
# if notes is end
if len(city_notes_url) == 0:
return
for city_note_url in city_notes_url:
try:
notes_url = 'http://www.mafengwo.cn%s'%(city_note_url[6:])
if notes_url in download_bf:
continue
pass
print 'download %s'%(notes_url)
req_note = urllib2.Request(notes_url,headers=request_headers)
response_note = urllib2.urlopen(req_note)
htmlcontent_note = response_note.read()
'''
print htmlcontent_note
htmlcontent_note_t = html.parse(htmlcontent_note)
title = htmlcontent_note_t.find('.//title').text
'''
filename = city_note_url[7:].replace('/','_')
#filename = title
#save html
fo = open("%s%s"%(dirname,filename), 'wb+')
fo.write(htmlcontent_note)
fo.close()
download_bf.add(notes_url)
except Exception,Arguments:
print Arguments
continue
pass
pass
pass
pass
if __name__ == '__main__':
if not os.path.exists(dirname):
os.makedirs(dirname)
pass
try:
#get city code
req = urllib2.Request("http://www.mafengwo.cn/mdd/",headers=request_headers)
response = urllib2.urlopen(req)
htmlcontent = response.read()
# get city homepage
city_home_pages = re.findall('/travel-scenic-spot/mafengwo/\d{5}.html',htmlcontent)
for city in city_home_pages:
city_code.append(city[29:34])
download_city_notes(city[29:34])
pass
print k
except urllib2.HTTPError,Arguments:
print Arguments
except httplib.BadStatusLine:
print 'BadStatusLine'
except Exception,Arguments:
print Arguments