Python网络爬虫阶段总结
学习python爬虫有一个月了,现在将学习的东西和遇到的问题做一个阶段总结,以作复习备用,另对于python爬虫感兴趣的,如果能帮到你们少走些弯路,那也是极好的。闲话少说,下面直接上干货:
Python学习网络爬虫主要分3个大的版块:抓取,分析,存储
另外,比较常用的爬虫框架Scrapy,这里最后也详细介绍一下。
当我们在浏览器中输入一个url后回车,后台会发生什么?
简单来说这段过程发生了以下四个步骤:
· 查找域名对应的IP地址。
· 向IP对应的服务器发送请求。
· 服务器响应请求,发回网页内容。
· 浏览器解析网页内容。
网络爬虫要做的,简单来说,就是实现浏览器的功能。通过指定url,直接返回给用户所需要的数据,而不需要一步步人工去操纵浏览器获取。
抓取
这一步,你要明确要得到的内容是什么?是HTML源码,还是Json格式的字符串等。
1. 最基本的抓取
抓取大多数情况属于get请求,即直接从对方服务器上获取数据。
首先,Python中自带urllib及urllib2这两个模块,基本上能满足一般的页面抓取。另外,requests也是非常有用的包,与此类似的,还有httplib2等等。
Requests:
import requests
response = requests.get(url)
content = requests.get(url).content
print "response headers:", response.headers
print "content:", content
Urllib2:(标签整齐,清晰,看着比较舒服,以后可以用它,便于阅读HTML)
import urllib2
response = urllib2.urlopen(url)
content = urllib2.urlopen(url).read()
print "response headers:", response.headers
print "content:", content
Httplib2:
import httplib2
http = httplib2.Http()
response_headers, content = http.request(url, 'GET')
print "response headers:", response_headers
print "content:", content
此外,对于带有查询字段的url,get请求一般会将来请求的数据附在url之后,以?分割url和传输数据,多个参数用&连接。
data = {'data1':'XXXXX', 'data2':'XXXXX'}
Requests:data为dict,json
import requests
response = requests.get(url=url, params=data)
Urllib2:data为string
import urllib, urllib2
data = urllib.urlencode(data)
full_url = url+'?'+data
response = urllib2.urlopen(full_url)
2. 对于登陆情况的处理
2.1 使用表单登陆
这种情况属于post请求,即先向服务器发送表单数据,服务器再将返回的cookie存入本地。
data = {'data1':'XXXXX', 'data2':'XXXXX'}
Requests:data为dict,json
import requests
response = requests.post(url=url, data=data)
Urllib2:data为string
import urllib, urllib2
data = urllib.urlencode(data)
req = urllib2.Request(url=url, data=data)
response = urllib2.urlopen(req)
2.2 使用cookie登陆
使用cookie登陆,服务器会认为你是一个已登陆的用户,所以就会返回给你一个已登陆的内容。因此,需要验证码的情况可以使用带验证码登陆的cookie解决。
import requests
requests_session = requests.session()
response = requests_session.post(url=url_login, data=data)
若存在验证码,此时采用response = requests_session.post(url=url_login, data=data)是不行的,做法应该如下:
response_captcha = requests_session.get(url=url_login, cookies=cookies)
response1 = requests.get(url_login) # 未登陆
response2 = requests_session.get(url_login) # 已登陆,因为之前拿到了Response Cookie!
response3 = requests_session.get(url_results) # 已登陆,因为之前拿到了Response Cookie!
3. 对于反爬虫机制的处理
3.1 使用代理
适用情况:限制IP地址情况,也可解决由于“频繁点击”而需要输入验证码登陆的情况。
这种情况最好的办法就是维护一个代理IP池,网上有很多免费的代理IP,良莠不齐,可以通过筛选找到能用的。对于“频繁点击”的情况,我们还可以通过限制爬虫访问网站的频率来避免被网站禁掉。
关键代码,如下几行:
proxies = {'http':'http://XX.XX.XX.XX:XXXX'}
Requests:
import requests
response = requests.get(url=url, proxies=proxies)
Urllib2:
import urllib2
proxy_support = urllib2.ProxyHandler(proxies)
opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
urllib2.install_opener(opener) # 安装opener,此后调用urlopen()时都会使用安装过的opener对象
response = urllib2.urlopen(url)
这在某些情况下比较有用,比如IP被封了,或者比如IP访问的次数受到限制等等。此时,可以在middlewares.py中通过类对代理IP进行封装,详细代码如下:
class ProxyMiddleware(object):
def process_request(self, request, spider):
proxy = random.choice(PROXIES)
if proxy['user_pass'] is not None:
request.meta['proxy'] = "http://%s" % proxy['ip_port']
encoded_user_pass = base64.encodestring(proxy['user_pass'])
request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass
print "**************ProxyMiddleware have pass************" + proxy['ip_port']
else:
print "**************ProxyMiddleware no pass************" + proxy['ip_port']
request.meta['proxy'] = "http://%s" % proxy['ip_port']
PROXIES = [
{'ip_port': '218.4.101.130:83', 'user_pass': ''},
{'ip_port': '113.121.47.97:808', 'user_pass': ''},
{'ip_port': '112.235.20.223:80', 'user_pass': ''},
{'ip_port': '27.151.30.68:808', 'user_pass': ''},
{'ip_port': '175.155.25.50:808', 'user_pass': ''},
{'ip_port': '222.85.50.207:808', 'user_pass': ''},
{'ip_port': '116.255.153.137:8082', 'user_pass': ''},
{'ip_port': '119.5.0.26:808', 'user_pass': ''},
{'ip_port': '183.32.88.223:808', 'user_pass': ''},
{'ip_port': '180.76.154.5:8888', 'user_pass': ''},
{'ip_port': '221.229.44.174:808', 'user_pass': ''},
{'ip_port': '27.151.30.68:808', 'user_pass': ''},
{'ip_port': '60.178.86.7:808', 'user_pass': ''},
{'ip_port': '58.243.104.149:8998', 'user_pass': ''},
{'ip_port': '120.27.49.85:8090', 'user_pass': ''},
]
注意,由于代理IP一般都有时效性,需要找到能用的代理IP将上面ip_port关键字对应的值替换下来。
3.2 时间设置
适用情况:限制频率情况。
Requests,Urllib2都可以使用time库的sleep()函数:
import time
time.sleep(1)
3.3 伪装成浏览器,或者反“反盗链”
有些网站会检查你是在使用真的浏览器访问,还是机器自动访问的。这种情况,加上User-Agent,表明你是浏览器访问即可。有时还会检查是否带Referer信息,还会检查你的Referer是否合法,一般再加上Referer。
headers = {'User-Agent':'XXXXX'} # 伪装成浏览器访问,适用于拒绝爬虫的网站
headers = {'Referer':'XXXXX'}
headers = {'User-Agent':'XXXXX', 'Referer':'XXXXX'}
Requests:
response = requests.get(url=url, headers=headers)
Urllib2:
import urllib, urllib2
req = urllib2.Request(url=url, headers=headers)
response = urllib2.urlopen(req)
详细的,可以在middlewares.py中通过类对代理(浏览器)进行封装
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
class RotateUserAgentMiddleware(UserAgentMiddleware):
def __init__(self, user_agent=''):
self.user_agent = user_agent
def process_request(self, request, spider):
ua = random.choice(self.user_agent_list)
if ua:
#print ua, '-----------------yyyyyyyyyyyyyyyyyyyyyyyyy'
request.headers.setdefault('User-Agent', ua)
# the default user_agent_list composes chrome,IE,firefox,Mozilla,opera,netscape
# for more user agent strings,you can find it in http://www.useragentstring.com/pages/useragentstring.php
user_agent_list = [ \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1" \
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", \
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", \
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", \
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", \
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", \
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]
4. 对于断线重连
不多说。
def multi_session(session, *arg):
retryTimes = 20
while retryTimes>0:
try:
return session.post(*arg)
except:
print '.',
retryTimes -= 1
或者
def multi_open(opener, *arg):
retryTimes = 20
while retryTimes>0:
try:
return opener.open(*arg)
except:
print '.',
retryTimes -= 1
这样我们就可以使用multi_session或multi_open对爬虫抓取的session或opener进行保持。
或者设置失败后自动重试
def get(self,req,retries=3):
try:
response = self.opener.open(req)
data = response.read()
except Exception , what:
print what,req
if retries>0:
return self.get(req,retries-1)
else:
print 'GET Failed',req
return ''
return data
5. 多进程抓取
单线程太慢的话,就需要多线程了,这里给个简单的线程池模板 这个程序只是简单地打印了1-10,但是可以看出是并发地。
from threading import Thread
from Queue import Queue
from time import sleep
#q是任务队列
#NUM是并发线程总数
#JOBS是有多少任务
q = Queue()
NUM = 2
JOBS = 10
#具体的处理函数,负责处理单个任务
def do_somthing_using(arguments):
print arguments
#这个是工作进程,负责不断从队列取数据并处理
def working():
while True:
arguments = q.get()
do_somthing_using(arguments)
sleep(1)
q.task_done()
#fork NUM个线程等待队列
for i in range(NUM):
t = Thread(target=working)
t.setDaemon(True)
t.start()
#把JOBS排入队列
for i in range(JOBS):
q.put(i)
#等待所有JOBS完成
q.join()
6. 对于Ajax请求的处理
对于“加载更多”情况,使用Ajax来传输很多数据。
它的工作原理是:从网页的url加载网页的源代码之后,会在浏览器里执行JavaScript程序。这些程序会加载更多的内容,“填充”到网页里。这就是为什么如果你直接去爬网页本身的url,你会找不到页面的实际内容。
这里,若使用Google Chrome分析”请求“对应的链接(方法:右键→审查元素→Network→清空,点击”加载更多“,出现对应的GET链接寻找Type为text/html的,点击,查看get参数或者复制Request URL),循环过程。
· 如果“请求”之前有页面,依据上一步的网址进行分析推导第1页。以此类推,抓取Ajax地址的数据。
· 对返回的json格式数据(str)进行正则匹配。json格式数据中,需从'\uxxxx'形式的unicode_escape编码转换成u'\uxxxx'的unicode编码。
7. 自动化测试工具Selenium
Selenium是一款自动化测试工具。它能实现操纵浏览器,包括字符填充、鼠标点击、获取元素、页面切换等一系列操作。总之,凡是浏览器能做的事,Selenium都能够做到。
如:如何在给定城市列表后,使用selenium来动态抓取去哪儿网的票价信息的代码?
8. 验证码识别
对于网站有验证码的情况,我们有三种办法:
· 使用代理,更新IP。
· 使用cookie登陆。
· 验证码识别。
使用代理和使用cookie登陆之前已经讲过,下面讲一下验证码识别。
可以利用开源的Tesseract-OCR系统进行验证码图片的下载及识别,将识别的字符传到爬虫系统进行模拟登陆。当然也可以将验证码图片上传到打码平台上进行识别。如果不成功,可以再次更新验证码识别,直到成功为止。
参考项目:验证码识别项目第一版:Captcha1
爬取有两个需要注意的问题:
· 如何监控一系列网站的更新情况,也就是说,如何进行增量式爬取?
· 对于海量数据,如何实现分布式爬取?
9.编码问题
在解析的过程中要注意编码问题,因为网页有UTF-8 编码的,也有GBK编码的,还有GB2312等等. 如果编码问题没有处理好,很有可能会导致输入输出异常,正则表达式匹配错误等问题.我的解决办法是坚持一个中心思想: "不管你是什么编码来的,到解析程序统一换成utf-8编码".比如有的网页是GBK编码,在处理之前我会先对它进行一个转码操作:
utf8_page = GBK_page.decode("GBK").encode("utf8")
同时在代码的初始化位置(或者是最开始部分)我一般会加上以下代码:
import sys
reload(sys)
sys.setdefaultencoding('utf8')
同时代码文件的编码方式也要保证是utf-8.
这样处理调理比较清晰,统一.不会出现一个utf-8的正则表达式和一个GBK的字符串做匹配最后啥也匹配不出来.或者输出的数据即有utf8编码的字符串,又有GBK编码的字符串导致IO错误.
如果事先不知道网页是什么编码,建议使用python 的第三方包chardet:https://pypi.python.org/pypi/chardet/ 它可以自动帮你识别出网页的编码.用法是:
import chardetimport urllib2
#可根据需要,选择不同的数据
TestData = urllib2.urlopen('http://www.baidu.com/').read()print chardet.detect(TestData)
分析
抓取之后就是对抓取的内容进行分析,你需要什么内容,就从中提炼出相关的内容来。
常见的分析工具有正则表达式,BeautifulSoup,lxml等等。
存储
分析出我们需要的内容之后,接下来就是存储了。
我们可以选择存入文本文件,也可以选择存入MySQL或MongoDB数据库等。
存储有两个需要注意的问题:
· 如何进行网页去重?
· 内容以什么形式存储?
Scrapy
Scrapy是一个基于Twisted的开源异构的Python爬虫框架,在工业中应用非常广泛。
Python网络爬虫阶段总结相关推荐
- python网络爬虫教程-终于明了python网络爬虫从入门到实践
Python是一款功能强大的脚本语言,具有丰富和强大的库,重要的是,它还具有很强的可读性,易用易学,非常适合编程初学者入门.以下是小编为你整理的python网络爬虫从入门到实践 环境配置:下载Pyth ...
- python与excel结合能做什么-Python网络爬虫与文本数据分析
原标题:Python网络爬虫与文本数据分析 课程介绍 在过去的两年间,Python一路高歌猛进,成功窜上"最火编程语言"的宝座.惊奇的是使用Python最多的人群其实不是程序员,而 ...
- python网络爬虫的基本步骤-python爬虫入门需要哪些基础/python 网络爬虫教程
如何入门 Python 爬虫 入门个吊,放弃 python爬虫入门需要哪些基础 现在之所以有多的小伙伴热衷于爬虫技术,无外乎是因为爬我们做很多事情,比如搜索引擎.采集数据.广告过滤等,以Python为 ...
- python网络爬虫技术课后答案_尔雅《Python网络爬虫技术(2020年秋季学期)》网课答案...
尔雅<Python网络爬虫技术(2020年秋季学期)>网课答案 法律的一般含义是() 答:法律是由国家创制并保证实施的行为规范 下列属于急症手术的是 答:肝破裂 脾气统摄血液的功能,实际上 ...
- 小猿学python_小猿圈详解小白如何学习Python网络爬虫
人工智能发展的今天,现在很多企业也都在学习python技术开发,但是真正会的却不是很多,特别是很多都喜欢爬虫,因为可以爬取一些自己喜欢的内容,那么对于小白的话该如何学习python爬虫呢?下面小猿圈P ...
- Python网络爬虫专业级框架_scrapy
首先感慨下当今的互联网,08年刚来北京工作的时候什么也没有.出去面试全凭一张纸质的北京地图跟一张嘴.学习还停留在看书的阶段(天天上下班的公交车上看书看到睡着,哈哈). 通过这段时间的学习,初步掌握了如 ...
- 人生苦短,我用 Python,如何学习 Python 网络爬虫?
人生苦短,我用 Python Python 网络爬虫上手很快,能够尽早入门,可是想精通确实是需求些时间,需求达到爬虫工程师的级别更是需求煞费苦心了,接下来共享的学习道路是针对小白或许学习 Python ...
- python网络爬虫教程(四):强大便捷的请求库requests详解与编程实战
上一章中,我们了解了urllib的基本用法,详情可浏览如下链接python网络爬虫教程(三):详解urllib库,但其中确实有不方便的地方,为此,我们可以使用更方便更简洁的HTTP请求库request ...
- 爬虫书籍-Python网络爬虫权威指南OCR库 NLTK 数据清洗 BeautifulSoup Lambda表达式 Scrapy 马尔可夫模型
Python网络爬虫权威指南 编辑推荐 适读人群 :需要抓取Web 数据的相关软件开发人员和研究人员 作为一种采集和理解网络上海量信息的方式,网页抓取技术变得越来越重要.而编写简单的自动化程序(网络爬 ...
- python网络爬虫_Python网络爬虫——爬取视频网站源视频!
原标题:Python网络爬虫--爬取视频网站源视频! 学习前提 1.了解python基础语法 2.了解re.selenium.BeautifulSoup.os.requests等python第三方库 ...
最新文章
- Linux驱动程序编写
- CSS3弹性盒模型之box-orient box-direction
- Spring Boot 入门之基础篇(一)
- 一点点学习PS--实战四
- 实验一 小凡和VMware虚拟机的使用练习
- JavaScript from C#(入门篇)
- Pandas CookBook -- 04选取数据子集
- 第三篇.python编辑器和集成环境01
- angular中的href=unsafe:我该怎么摆脱你的溺爱!!
- java spring 过滤器_java – 如何在spring-security中的另一个过滤器之前添加过滤器?...
- Curie Module是什么
- Vue和React组件之间的传值方式
- 人脸识别的代码及问题
- 浅谈机器学习-分类和聚类的区别
- linux虚拟机简单部署以及安装可视化界面
- DSP CCS软件仿真
- 金蝶K3工资模块个税计算公式
- QHeaderView 表头设置QWidget控件
- 2022年打工人转行实录!你后悔转行了吗?
- 赤霉素3β-羟化酶的下调增强了大豆的光合作用并提高了种子产量