导图社区 精通Python网络爬虫
<<精通python网络爬虫>>精简了书籍内容,必要的代码都放到备注上面了。代码经过我的测试,都可以运行,达到相同的效果。如果有不明白或者有错误要指出
编辑于2019-07-16 02:58:57程序猿知识技能总结大全,java、python、正则表达式、Linux等多方面知识导图
包含抖音运营、短视频运营要点、运营之光、从零开始做运营、学会写作等多个运营读书笔记、运营知识框架等
产品、运营工具包大全:办公工具、文字工具、音频工具、视频工具、社群工具、公众号辅助工具、排版工具、图片工具、H5工具、小程序工具等
项目管理流程竖版思维导图、从需求收集、需求评审、规划阶段、再到方案实现阶段、测试阶段、至最后的上线阶段,详细描述每个阶段的工作内容
产品需求文档(PRD)是确保团队理解项目需求和目标的关键文档。在撰写PRD时,要明确项目背景、需求类型,遵循有理有据、全面清晰、易读的写作原则。选择适当的工具如Axure或Word,包括文档修改记录、项目背景、名词解释、流程图、需求说明等内容。使用原型工具结合需求,提供清晰的视觉参考。详细描述每个功能点,包括权限、规则逻辑、极值、交互等。使用标点符号、示例、标记重要内容,提高文档可读性。最终,根据项目需求决定是否包含额外的内容,以确保PRD有效指导项目开发。
系统集成项目管理工程师-十大管理输入、输出、工具总结,软考必备复习导图
社区模板帮助中心,点此进入>>
程序猿知识技能总结大全,java、python、正则表达式、Linux等多方面知识导图
包含抖音运营、短视频运营要点、运营之光、从零开始做运营、学会写作等多个运营读书笔记、运营知识框架等
产品、运营工具包大全:办公工具、文字工具、音频工具、视频工具、社群工具、公众号辅助工具、排版工具、图片工具、H5工具、小程序工具等
项目管理流程竖版思维导图、从需求收集、需求评审、规划阶段、再到方案实现阶段、测试阶段、至最后的上线阶段,详细描述每个阶段的工作内容
产品需求文档(PRD)是确保团队理解项目需求和目标的关键文档。在撰写PRD时,要明确项目背景、需求类型,遵循有理有据、全面清晰、易读的写作原则。选择适当的工具如Axure或Word,包括文档修改记录、项目背景、名词解释、流程图、需求说明等内容。使用原型工具结合需求,提供清晰的视觉参考。详细描述每个功能点,包括权限、规则逻辑、极值、交互等。使用标点符号、示例、标记重要内容,提高文档可读性。最终,根据项目需求决定是否包含额外的内容,以确保PRD有效指导项目开发。
系统集成项目管理工程师-十大管理输入、输出、工具总结,软考必备复习导图
理论基础篇
什么是网络爬虫
初识网络爬虫
自动化浏览网页中信息的程序
为什么要学网络爬虫
私人订制搜索引擎,深入了解搜索引擎
大数据时代的数据分析
对seo从业者来说,可以了解原理,更好优化
解决就业
网络爬虫的组成
控制节点
爬虫的中央控制器
爬虫节点
根据一定的算法浏览网页内容
资源库
爬取到的结果存储到资源库
网络爬虫的类型
通用网络爬虫
全网爬取,爬取的数据是海量
聚焦网络爬虫
主题爬取,有选择的爬取
增量式爬虫
更新的时候只改变更新的地方
深层网络爬虫
网页分为表层和深层,不需要提交表单的静态页面就是表层,而需要提交表单的就是深层
爬虫扩展——聚焦爬虫
组成
初始url集合
url队列
页面爬行模块
页面分析模块
页面数据库
连接过滤模块
内容评价模块
链接评价模块
图解
网络爬虫技能总览
网络爬虫技能总览图
搜索引擎核心工作流程
图解
过程
爬虫
通过控制器存储到原始数据库
索引器
建立索引,将原始数据库的内容存储到索引数据库
用户
通过用户交互接口查询信息,检索器从索引数据库取得想要结果
同时用户的行为,信息会存储到用户日志数据库中
日志分析器
分析用户日志数据库,通过得到的结果,调整原始数据库以及索引数据库的排名结果,以及其他
用户爬虫的那些事儿
用户爬虫爬取的是用户的信息
知乎注册用户,qq使用用户,淘宝信息爬取等等
核心技术篇
网络爬虫实现原理与实现技术
网络爬虫实现原理详解
通用爬虫
聚焦爬虫
爬行策略
深度与广度
网站层次示意图
深度优先爬取策略
ade->b->fg
广度优先爬取策略
abc->de->fg
大站优先爬取策略
对网页归类,网站多的归类到大站,先爬取这种站点
反链爬取策略
反向链接多的网站优先爬取,但是这种有可能出现作弊,所以要反向链接数可靠,才采取这种策略
其他策略
opic策略
PartialPageRank策略
了解
网页更新策略
用户体验策略
历史数据策略
聚焦分析策略
网页分析算法
基于用户行为的
基于网络拓扑的
基于页面内容的
身份识别
告诉站长自己的身份,user-agent
网络爬虫实现技术
python,java,php,go,c++等等
实例——metaseeker
metaseeker是一款网页采集程序,了解即可
Urllib库与URLError异常处理
什么是Urllib库
操作网页的模块
python2.x里有urllib,也有urlib2,python3.x版本将两者合并了
快速使用Urllib爬取网页
urlopen访问百度并保存为html页面
``` import urllib.request #导入相应的模块 file=urllib.request.urlopen("http://www.baidu.com") #访问百度页面 data=file.read() #读取返回的全部 dataline=file.readline()#读取返回的一行 print(dataline) #打印这一行 fhandle=open("urllib_urlopen.html","wb") #打开一个文件 fhandle.write(data) #将百度的源码传递进去 fhandle.close()#关闭文件 ```
urlretrieve直接访问网页并保存
``` import urllib.request #urlretrieve方法可以直接将源码保存到文件中 urllib.request.urlretrieve("http://edu.51cto.com",filename="urllib_urlretrieve.html") #urlretrieve方法使用的时候会产生一些缓存,用urlcleanup即可清除缓存 urllib.request.urlcleanup() ```
其他(取环境,状态码,取网址,编码网址)
``` import urllib.request file=urllib.request.urlopen("http://www.baidu.com") #获取当前网页返回的状态码,用getcode statucode=str(file.getcode()) print("当前状态码是:"+statucode) #获取当前网页的网址,用geturl url=file.geturl() print("当前网址是:"+url) #对url进行url编码,用quote urlbefore="http://www.test.com" urlafter=urllib.request.quote(urlbefore) print("之前的网址是:"+urlbefore+"经过编码之后的网址是:"+urlafter) #获取当前网页的环境信息,用info() info=str(file.info()) print("当前的环境是:\n"+info) #解码成utf8 data=file.read().decode("UTF-8") ```
浏览器的模拟——Headers属性
使用build_opener修改报头
``` import urllib.request url="https://blog.csdn.net/blogdevteam/article/details/80819627" headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") #创建一个打开对象 opener=urllib.request.build_opener() #增加报头 opener.addheaders=[headers] #用打开对象打开网址,并获取内容 data=opener.open(url).read() #以二进制写的模式打开文件 fhandle=open("urllib_opener.html","wb") #将数据写入文件 fhandle.write(data) #关闭文件 fhandle.close() ```
创建Request对象,用add_header方法
``` import urllib.request url="https://blog.csdn.net/blogdevteam/article/details/80819627" headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") #创建已给Request对象,对这个对象进行数据设置,然后将其传递进urlopen方法中 req=urllib.request.Request(url) req.add_header("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") #访问网页,并获取数据 data=urllib.request.urlopen(req).read() #以二进制写的模式打开文件 fhandle=open("urllib_Request.html","wb") #将数据写入文件 fhandle.write(data) #关闭文件 fhandle.close() ```
超时设置
有时候访问网页,如果超过一定时间,那么我们可以判定这个网页超时了
``` import urllib.request url="http://www.baidu.com" #将timeout设置成0.1秒,这个对一个网也来说是会很快的,所以会看到结果是网页异常,这个时间可以设置大一点 try: urllib.request.urlopen(url,timeout=0.1) except Exception as e: print("网页异常") ```
HTTP协议请求实战
get-实现百度查询
``` import urllib.request #这个是关键词,可以随意输入 keywd="百度百科" #如果关键词是中文的,要进行编码转换 url_keywd=urllib.request.quote(keywd) url="http://www.baidu.com/s?wd="+url_keywd req=urllib.request.Request(url) data=urllib.request.urlopen(req).read() #将内容写入html fhandle=open("urllib_get.html","wb") fhandle.write(data) fhandle.close() ```
post-表单提交
``` import urllib.request import urllib.parse url="http://www.iqianyue.com/mypost" #要用字典构造表单,这个是原始表单 postdata_a={ "name":"ceo@iqianyue.com", "pass":"aA123456" } #要传递表单,还需要对数据进行转码,转码成utf8的url编码 postdata_b=urllib.parse.urlencode(postdata_a).encode("utf-8") #创建一个Request对象 req=urllib.request.Request(url,postdata_b) #增加浏览器报文 req.add_header("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") #提交表单 data=urllib.request.urlopen(req).read() #将内容写入html fhandle=open("urllib_post.html","wb") fhandle.write(data) fhandle.close() ```
代理服务器的设置
代理ip访问百度
``` import urllib.request def use_proxy(proxy_addr,url): #设置代理地址信息 proxy=urllib.request.ProxyHandler({'http':proxy_addr}) #创建一个opener对象,第一个参数是代理信息,第二个是http处理器,Handler,这个是用来处理http请求的 #还有httpshandler,处理https请求的 opener=urllib.request.build_opener(proxy,urllib.request.HTTPHandler) #安装全局默认的opener对象,这个全局安装之后,之后的urlopen,都会调用这个对象,不用opener.open这种格式 urllib.request.install_opener(opener) data=urllib.request.urlopen(url).read().decode("utf-8") return data #代理的地址,ip:端口号,如果代理ip不可用,会报错 proxy_addr="139.208.194.45:8118" data=use_proxy(proxy_addr,"http://www.baidu.com") #打印data内容长度 print(len(data)) ```
代理地址
<a href="http://www.xicidaili.com/" target="blank">西刺代理</a>
DebugLog实战
边运行边打印调试日志(51cto首页)
为什么要这么做,书里只描述了一个步骤 ``` import urllib.request httphd=urllib.request.HTTPHandler(debuglevel=1) httpshd=urllib.request.HTTPSHandler(debuglevel=1) opener=urllib.request.build_opener(httphd,httpshd) urllib.request.install_opener(opener) data=urllib.request.urlopen("http://edu.51cto.com") ```
异常处理神器——URLError实战
URlError
因为书中说csdn是禁止爬取的,实际上是可以爬取的并不会报错,所以这里找了一篇文章,网址后面加了一个数字,这样就会看到错误 ``` import urllib.request import urllib.error try: urllib.request.urlopen("https://blog.csdn.net/wys7250578/article/details/450221610") except urllib.error.URLError as e: print(e.reason) ```
产生错误的可能
连接不上服务器
连接不到特定的服务器
无网络
触发了HTTPError
HTTPError
这是URLError的子类,它有一个code属性,这个就是请求返回的状态码
如果出现的不是HTTPError的情况,URLError就会没有code属性,会报错,可以断网看效果
两个异常都处理
``` import urllib.request import urllib.error try: urllib.request.urlopen("https://blog.csdn.net/wys7250578/article/details/450221610") except urllib.error.HTTPError as e: print(e.code) print(e.reason) except urllib.error.URLError as e: print(e.reason) ```
正则表达式与Cookie的使用
正则基础(看书)
什么是正则表达式
正则表达式基础知识
正则表达式常见函数
python使用re模块来操作正则
只返回一个结果
match
match必须从起始位置就匹配,不然会返回None ``` import re string="dpythonasdfsdf" pattern=".python." result=re.match(pattern,string) #打印匹配信息 print(result) #打印匹配位置 print(result.span()) #打印匹配结果 print(result.group()) ```
search
search与match不同,它全文检索,而不是从起始位置 ``` import re string="asdfsadfsadpythonasdfsdf" pattern=".python." result=re.match(pattern,string) result2=re.search(pattern,string) print("使用match的结果:"+str(result)) print("使用search的结果:"+str(result2)) print("匹配到的结果是"+str(result2.group())) ```
多个结果
findall
match和search.只会返回一个结果,有时候我们需要的不只是一个结果,因此,需要用findall,它会返回一个列表 ``` import re string="aasfsa1python1safdsadf2python2asdfsadf3python3" pattern=".python." #先生成一个预编译对象 reg=re.compile(pattern) result=reg.findall(string) print(result) ```
替换
sub
有时候需要替换文本中的字符,要用到sub ``` import re string="你好吗,,asdf,,你好吗,,asdfsaf,你好吗,," pattern="你好吗." #全部替换 result=re.sub(pattern,"呵呵",string) #只替换两次 result2=re.sub(pattern,"呵呵",string,2) print(result) print(result2) ```
常见实例解析
匹配.com,.cn后缀的网址
``` import re pattern="[a-zA-Z]+://[^\s]*[.com|.cn]" string="<a href='http://www.baidu.com'>百度一下</a>" result=re.search(pattern,string) print(result) ```
匹配电话
``` import re pattern="\d{4}-\d{7}" string="0731-8112121" result=re.search(pattern,string) print(result) ```
什么是Cookie
cookie是客户端保存的会话信息,与之对应的session是服务端保存的会话信息
Cookiejar实战精析
``` import urllib.request import urllib.parse import http.cookiejar url="http://dx31.wzdr.cn/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1" #转换后表单数据 postdata_a="fastloginfield=username&username=wzdr1&password=wzdrwzdr&quickforward=yes&handlekey=ls" #把字符串转成字典 list1=postdata_a.split("&") list2={} for x in list1: name,value=x.split("=",2) print(name,value) list2[name]=value #转码字符串 postdata_b=urllib.parse.urlencode(list2).encode("gbk") #构建一个Request对象 req=urllib.request.Request(url,postdata_b) req.add_header("user-agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") #创建一个cookiejar对象 cjar=http.cookiejar.CookieJar() #创建一个cookie处理器 chandler=urllib.request.HTTPCookieProcessor(cjar) #将处理器传进opener opener=urllib.request.build_opener(chandler) #安装全局opener urllib.request.install_opener(opener) #打开网页 file=opener.open(req) data=file.read() # #写到文件 file=open("urllib_cookejar1.html","wb") file.write(data) file.close() #登录后访问页面,看是否有个人信息 response=urllib.request.urlopen("http://dx31.wzdr.cn/forum.php") data=response.read() file=open("urllib_cookiejar2.html","wb") file.write(data) file.close() ```
手写Python爬虫
图片爬虫实战
使用时,在文件目录下建立一个文件夹image ``` import re import urllib.request def craw(url,page): html1=urllib.request.urlopen(url).read() html1=html1.decode("utf-8") #过滤掉侧边栏的信息,只取主要信息 pat1='<div id="plist" class="[\s\S]*?<div class="page clearfix">' result1=re.compile(pat1).findall(html1) request1=result1[0] #取图片网址,因为主要信息中,有六十张图片,前面十张和后面十张是不同的格式,所以用两个正则取 pat2='<img width="220" height="220" data-img="1" src="(.*?)">' pat3='<img width="220" height="220" data-img="1" data-lazy-img="(.*?)">' imagelist1=re.compile(pat2).findall(request1) imagelist2=re.compile(pat3).findall(request1) #拼接两个图片列表 imagelist3=imagelist1+imagelist2 x=1 for imageurl in imagelist3: imagename="image/"+str(page)+str(x)+".jpg" imageurl="http:"+imageurl print("正在爬取第"+str(i)+"张图片") try: urllib.request.urlretrieve(imageurl,filename=imagename) except urllib.error.URLError as e: if hasattr(e,"code"): x+=1 if hasattr(e,"reason"): x+=1 x+=1 #这里可以通过访问第一页获取range范围,为了不产生过多图片,范围定为1-2 for i in range (1,2): url="https://list.jd.com/list.html?cat=9987,653,655&page="+str(i) print(url) craw(url,i) ```
链接爬虫实战
csdn是动态刷新的,而且每次打开都是不一样的,向下翻页会产生新的数据包,这里不翻页,只采集最开始的 ``` import re import urllib.request def getlink(url): #创建一个opener headers=("user-agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") opener=urllib.request.build_opener() opener.addheaders=[headers] urllib.request.install_opener(opener) #安装全局opener file=urllib.request.urlopen(url) data=file.read().decode("utf-8") #取网址列表 pat='data-track-click=\'{"mod":".*?","con":",(.*?),.*?"}\'>\s*?([^\s]*?)\s*?</a>\s*?</h2>' link=re.compile(pat).findall(str(data)) #set可以去重复 link=list(set(link)) return link url="https://blog.csdn.net/" linklist=getlink(url) for link in linklist: url=link[0] title=link[1] #标题中有&mdash的进行替换 if(title.find("—")!=-1): title=title.replace("—","—") print("链接是%s\n标题是:%s"%(url,title)) print("\n") ```
糗事百科爬虫实战
因为糗事百科不仅仅是文字,还有图片,就连纯文字板块都有很多图片,因此换了一个网站.笑话集的原创笑话``http://www.jokeji.cn/yuanchuangxiaohua/list/default.htm`` ``` import re import os import urllib.request def getjoke(url): #创建一个opener headers=("user-agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") opener=urllib.request.build_opener() opener.addheaders=[headers] #安装全局opener urllib.request.install_opener(opener) #取得笑话 response=urllib.request.urlopen(url) data=response.read().decode("gb18030") pat='<h2><a href=".*?" target="_blank">(.*?)</a></h2>[\s\S]*?<li>([\s\S]*?)<i>.*?</i></li>' jokes=re.compile(pat).findall(data) return jokes #创建一个笑话目录 if os.path.exists("jokes")!=True: os.mkdir("jokes") #一共有七千多页,这里只拿两页进行测试 for i in range(1,3): url="http://www.jokeji.cn/yuanchuangxiaohua/list/default"+str(i)+".htm" jokes=getjoke(url) txtname="jokes\\jokepage_"+str(i)+".txt" #打开文件,用追加模式 file=open(txtname,"a") jokenumber=1 for joke in jokes: joketitle=joke[0] jokecontent=joke[1] #替换换行,以及一些特殊字符 jokecontent=jokecontent.replace("<BR>","\n") jokecontent=jokecontent.replace("<br>","\n") jokecontent=jokecontent.replace(" "," ") #每一页有十五个笑话,用一个txt保存一页的笑话,保存的格式是序号+题目+换行+内容+两个换行 file.write(str(jokenumber)+":"+joketitle+"\n"+jokecontent+"\n\n") #打印正在采集的数字 print("正在采集第"+str(i)+"页"+"第"+str(jokenumber)+"个笑话") jokenumber+=1 file.close() ```
微信爬虫实战
代码与书上有很大的不同,书上的是所有的文章和内容都存储到一个html里,而这段代码是每一个标题做一个文件名,文章写进html中 ``` import re import urllib.request import time import urllib.error import os def use_proxy(url,proxy_addr=""): try: if(proxy_addr!=""): proxy-urllib.request.ProxyHandler({'http':proxy_addr}) opener=urllib.request.build_opener(porxy,urllib.request.HTTPHandler) urllib.request.install_opener(opener) data=urllib.request.urlopen(url).read() return data except urllib.error.URLError as e: if hasattr(e,"code"): print(e.code) if hasattr(e,"reason"): print(e.reason) #发生了URLErro就延迟10秒 time.sleep(10) except Exception as e: print("exception:"+str(e)) #没有发生URLError就延迟1秒 time.sleep(1) def getlisturl(key,pagestart,pageend,proxy): page=pagestart keycode=urllib.request.quote(key) for page in range(pagestart,pageend+1): #进入搜狗微信搜索的具体页面 url="http://weixin.sogou.com/weixin?query="+keycode+"&type=2&page="+str(page) print("当前网址"+url) data1=use_proxy(url,proxy) #取出文章网址列表 listurlpat='<div class="txt-box">\s<h3>[\s\S]*?<a target="_blank" href="(.*?)"' #预编译的re.S代表的是把字符串看成一个整体,由此可以跨行匹配 #这个语句追加的是一个列表数据 listurl.append(re.compile(listurlpat,re.S).findall(data1.decode("utf-8"))) print("获取第"+str(page)+"页") return listurl def getcontent(listurl,proxy): for i in range(0,len(listurl)): for j in range(0,len(listurl[i])): #取的文章的网址 url=listurl[i][j] #把网址中的特殊字符替换 url=url.replace("amp;","") data=use_proxy(url,proxy) #取出标题 titlepat='var msg_title = "(.*?)";' title=re.compile(titlepat).findall(data.decode("utf-8")) #这里设置一个初始的标题值 thistitle="没有取到标题" if(title!=[]): #标题不为空,则写文章到html中 thistitle=title[0] htmlname="weixinarticle\\"+thistitle+".html" file=open(htmlname,"wb") file.write(data) file.close() print("正在处理第"+str(i+1)+"页--第"+str(j+1)+"篇--"+thistitle) #模拟浏览器 headers=("user-agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") opener=urllib.request.build_opener() opener.addheaders=[headers] #安装全局opener urllib.request.install_opener(opener) #设置空网址列表 listurl=[] key="物联网" #代理地址,这里没设置,有需要的就设置,调用的子程序判断不为空才会启用代理 proxy="" pagestart=1 pageend=1 listurl=getlisturl(key,pagestart,pageend,proxy) #创建一个文章目录 if os.path.exists("weixinarticle")==False: os.mkdir("weixinarticle") getcontent(listurl,proxy) ```
多线程爬虫
什么是多线程
程序的某一部分任务可以用多个线程来并行执行
多线程爬虫实战
简单例子
与书上代码不一样的是,加了一个sleep,这样可以很直观的看出来,不是A执行完了才执行B,而是两个交替执行 ``` import threading import time class A(threading.Thread): def __init__(self): threading.Thread.__init__(self) #初始化该线程 def run(self): for i in range(10): print("我是线程A") time.sleep(1) class B(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): for i in range(10): print("我是线程B") time.sleep(1) #实例化线程A为t1,B2为t2 t1=A() t2=B() #启动线程t1 t1.start() t2.start() ```
队列
队列是一种数据结构,在多线程中可以起很好的作用 ``` import queue #创建一个队列 a=queue.Queue() #传递数据队列,这个过程非常像排队,排在前面的就先出来 a.put("网址1") a.put("网址2") a.put("网址3") a.put("网址4") #表示入队列任务完毕 a.task_done() #队列先进先出,所以永远是取第一个,像超市结账,结账一个就走一个,人就不在了.结账完了,整个队列就空了 #上面排了四个,循环四次,取出队列中第一个用get for i in range(4): print(a.get()) print("出队列完毕") ```
微信爬虫多线程版
这里的多线程是把执行的线程分为三个,一个是获取网址的线程,不停往队列里放网址,一个是获取标题和内容的线程,不停从队列里取网址,一个是判断线程执行情况的线程,不停判断队列是否为空.这样就由一个线程变成了三个线程 ``` import re import urllib.request import time import urllib.error import os import threading import queue def use_proxy(url,proxy_addr=""): try: if(proxy_addr!=""): proxy-urllib.request.ProxyHandler({'http':proxy_addr}) opener=urllib.request.build_opener(porxy,urllib.request.HTTPHandler) urllib.request.install_opener(opener) data=urllib.request.urlopen(url).read() return data except urllib.error.URLError as e: if hasattr(e,"code"): print(e.code) if hasattr(e,"reason"): print(e.reason) #发生了URLErro就延迟10秒 time.sleep(10) except Exception as e: print("exception:"+str(e)) #没有发生URLError就延迟1秒 time.sleep(1) #这个线程用来获取每一页的网址放到队列中 class geturl(threading.Thread): def __init__(self,key,pagestart,pageend,proxy,urlqueue): threading.Thread.__init__(self) self.pagestart=pagestart self.pageend=pageend self.proxy=proxy self.urlqueue=urlqueue self.key=key def run(self): listurl=[] keycode=urllib.request.quote(key) #进入搜狗微信搜索的具体页面 for page in range(pagestart,pageend+1): url="http://weixin.sogou.com/weixin?query="+keycode+"&type=2&page="+str(page) print("当前网址"+url) data1=use_proxy(url,proxy) #取出文章网址列表 listurlpat='<div class="txt-box">\s<h3>[\s\S]*?<a target="_blank" href="(.*?)"' #预编译的re.S代表的是把字符串看成一个整体,由此可以跨行匹配 listurl=re.compile(listurlpat,re.S).findall(data1.decode("utf-8")) print("成功获取到了"+str(len(listurl))+"页") for link in listurl: #把网址中的特殊字符替换 link=link.replace("amp;","") self.urlqueue.put(link) self.urlqueue.task_done() #这个线程用来取出队列里的每一条网址,并访问获取标题内容 class getcontent(threading.Thread): def __init__(self,urlqueue,proxy): threading.Thread.__init__(self) self.urlqueue=urlqueue self.proxy=proxy def run(self): while(True): #取得网址 url=urlqueue.get() data=use_proxy(url,proxy) #取出标题 titlepat='var msg_title = "(.*?)";' title=re.compile(titlepat).findall(data.decode("utf-8")) #这里设置一个初始的标题值 thistitle="没有取到标题" if(title!=[]): #标题不为空,则写文章到html中 thistitle=title[0] htmlname="weixinarticle\\"+thistitle+".html" file=open(htmlname,"wb") file.write(data) file.close() print("正在爬取的文章是"+"--"+thistitle) #这个线程用来检查队列的执行情况,每隔一段时间检测一次,如果为空,说明任务执行完毕 class control(threading.Thread): def __init__(self,urlqueue): threading.Thread.__init__(self) self.urlqueue=urlqueue def run(self): while(True): print("程序执行中") #设定间隔为30秒 time.sleep(30) if(self.urlqueue.empty()): print("程序执行完毕") exit() #模拟浏览器 headers=("user-agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") opener=urllib.request.build_opener() opener.addheaders=[headers] #安装全局opener urllib.request.install_opener(opener) #设置空网址列表 listurl=[] #创建一个队列 urlqueue=queue.Queue() key="物联网" #代理地址,这里没设置,有需要的就设置,调用的子程序判断不为空才会启用代理 proxy="" pagestart=1 pageend=2 #如果不存在,创建一个目录文章目录 if os.path.exists("weixinarticle")==False: os.mkdir("weixinarticle") t1=geturl(key,pagestart,pageend,proxy,urlqueue) t2=getcontent(urlqueue,proxy) t3=control(urlqueue) t1.start() t2.start() t3.start() ```
学会使用Fiddler
什么是Fiddler
一款抓包软件,用来截获浏览器客户端与外界之间通信的数据
其他
浏览器自带调试f12
不支持复杂抓包
wireshark
功能比较齐全
爬虫与Fiddler的关系
fiddler可以更好的分析网页,使得我们能够编写更好的爬虫
自行了解
Fiddler的基本原理与基本界面
Fiddler捕获会话功能
使用QuickExec命令行
Fiddler断点功能
Fiddler会话查找功能
Fiddler的其他功能
爬虫的浏览器伪装技术
什么是浏览器伪装技术
网站识别出是非浏览器访问的,可能会禁止访问,这时候就需要伪装
常见的反爬虫机制
分析headers
user-agent
浏览器
referer
引用来源页
检测用户行为,比如同一ip频繁访问
这个可以通过代理ip
增加动态页面增加爬虫难度
可以通过第三方工具软件破解
本书主要针对第一种机制
浏览器伪装技术准备工作
了解headers,了解协议中常见字段代表什么意思
爬虫的浏览器伪装技术实战
无伪装程序
随便找的一篇网易的文章,这个程序抓包运行后,可以看到fiddler里的user-agent是python-urllib/3.6,并非浏览器 ``` import urllib.request import http.cookiejar url="http://news.163.com/18/0703/13/DLPV78UP00018AOQ.html" #fiddler的代理地址是127.0.0.1:8888 proxy=urllib.request.ProxyHandler({'http':"127.0.0.1:8888"}) cjar=http.cookiejar.CookieJar() opener=urllib.request.build_opener(proxy,urllib.request.HTTPHandler,urllib.request.HTTPCookieProcessor(cjar)) urllib.request.install_opener(opener) response=urllib.request.urlopen(url) #写到文件 data=response.read() fhandle=open("fiddle_test1.html","wb") fhandle.write(data) fhandle.close() ```
有伪装程序
通过headers添加,再抓包,就可以看到不再是python,而是设定的值 ``` import urllib.request import http.cookiejar #随便找的一篇网易的文章 url="http://news.163.com/18/0703/13/DLPV78UP00018AOQ.html" #fiddler的代理地址是127.0.0.1:8888 #以字典的形式设置headers headers={"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language":"zh-CN,zh;q=0.8", "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36", "Connection":"keep-alive" } proxy=urllib.request.ProxyHandler({'http':"127.0.0.1:8888"}) cjar=http.cookiejar.CookieJar() opener=urllib.request.build_opener(proxy,urllib.request.HTTPHandler,urllib.request.HTTPCookieProcessor(cjar)) #因为addheaders是列表中包裹着元组,所以要把上面的字典headers做处理 headerall=[] for name,value in headers.items(): headerall.append((name,value)) opener.addheaders=headerall urllib.request.install_opener(opener) response=urllib.request.urlopen(url) data=response.read() fhandle=open("fiddle_test1.html","wb") fhandle.write(data) fhandle.close() ```
爬虫的定向爬取技术
什么是爬虫的定向爬取技术
根据设定的主题,对网页和内容进行筛选
需要解决三个问题
定义好目标,和主题
建立好网站的筛选和内容过滤
建立好url排序算法
定向爬取的相关步骤与策略
步骤
理清目标
设置网址过滤机制
设置内容过滤机制
规划采集任务
结果修正
结果后续处理
图解
信息筛选策略
正则表达式
Xpath
Xpath是Xml的路径语言,在Scapy框架中经常用到
xlst
XSLT 指 XSL 转换,可以把xml转换成其他文档
定向爬取实战
腾讯视频评论批量采集并加载评论
``` import urllib.request import http.cookiejar import urllib.error import re def craw(vid,comid): try: url="http://coral.qq.com/article/"+vid+"/comment?commentid="+comid+"&reqnum=20" data=urllib.request.urlopen(url).read() except urllib.error.URLError as e: if hasattr(e,"code"): print(e.code) if hasattr(e,"reason"): print(e.reason) except Exception as e: print(e.message) return data #以字典的形式设置headers headers={"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language":"zh-CN,zh;q=0.8", "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36", "Connection":"keep-alive" } cjar=http.cookiejar.CookieJar() opener=urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cjar)) #因为addheaders是列表中包裹着元组,所以要把上面的字典headers做处理 headerall=[] for name,value in headers.items(): headerall.append((name,value)) opener.addheaders=headerall urllib.request.install_opener(opener) #视频id vid="1301388867" #评论的id,初始为零就是从最开始,后续还想要加载更多,就需要把最后一个id传递给加载的网址 comid="0" #加载的条数,这里设置为20 idpat='"id":"(.*?)"' userpat='"nick":"(.*?)"' compat='"content":"(.*?)"' for i in range(1,4): print("第"+str(i)+"页评论内容") print("---------------------") datacontent=craw(vid,comid) #书上下面的代码是放到循环里的,这样不能说错,但是每次循环都会取一次列表,这样增加了程序的开销,所以放到外面来 idlist=re.compile(idpat,re.S).findall(datacontent.decode("utf-8")) userlist=re.compile(userpat,re.S).findall(datacontent.decode("utf-8")) comlist=re.compile(compat,re.S).findall(datacontent.decode("utf-8")) for j in range(0,20): try: print("用户名是:"+eval('u"'+userlist[j]+'"')) except: print("用户名有特殊字符,无法解码:"+userlist[j]) print("评论内容:"+eval('u"'+comlist[j]+'"')) print("\n") #把当前页的最后一个id赋值给comid comid=idlist[19] ```
框架实现篇
了解Python爬虫框架
什么是Python爬虫框架
一些爬虫项目的半成品,留下了一些接口
框架中已经实现了一些常用的功能
常见的Python爬虫框架
Scrapy框架
Crawley框架
Portia框架
newspaper框架
Python—goose框架
爬虫利器——Scrapy安装与配置
在Windows7下安装及配置Scrapy实战详解
命令行下:pip instal scrapy
在Linux(Centos)下安装及配置Scrapy实战详解
在MAC下安装及配置Scrapy实战详解
开启Scrapy爬虫项目之旅
认识Scrapy项目的目录结构
项目文件夹
同名项目文件夹
spiders文件夹
__init__.py
项目中,爬虫部分的初始化,主要是对spiders的初始化
还有其他的爬虫文件
__init__.py
项目的初始化信息
items.py
爬虫项目的数据容器文件,主要用来定义获取的数据
pipelines.py
项目的管道文件,主要用来对item是里的数据进行进一步的加工
settings.py
爬虫项目的一些设置信息
middleware.py
下载中间件
Scrapy.cfg
放置爬虫的配置文件
用Scrapy进行爬虫项目管理
命令行进入文件夹
scrapy startproject 项目名
常用参数
--logfile=FILE
用来指定日志文件 ``scrapy startproject --file="../log.txt" 项目名`` 就是在当前上一个目录下建立一个日志文件,打开的时候是有信息的
--loglevel=LEVEL, -L LEVEL
设置日志的等级,这个有五个等级 1. DEBUG 输出调试信息,用于开发阶段默认是这个阶段 2. INFO 输出提示信息 3. WARNING 出现一些警告信息,存在前在错误 4. ERROR 发生了必须处理的错误 5. CRITICAL发生最严重的错误 命令: ``scrapy startproject -L INFO 项目名`` ``scrapy startproject --loglevel=INFO 项目名``
--nolog
控制不输出日志 ``scrapy startproject --nolog 项目名``
常用工具命令
全局命令
不需要进入项目文件下就可以运行的命令
常用
fetch
fetch用来显示爬取对应网址的过程 如果在项目之外就会调用默认的爬虫,如果是在项目之内就会调用项目内的爬虫 访问百度显示协议头,不显示日志 ``scrapy fetch --headers --nolog http://www.baidu.com``
runspider
runspider可以不依托scrapy项目,来运行一个爬虫文件 下面的测试源码,可以放在其他的任何地方,这里假设是text.py ``` from scrapy.spiders import Spider class FirstSpider(Spider): name="first" allowed_doman=["baidu.com"] start_urls=[ "http://www.baidu.com" ] def parse(self,response): pass ``` 处理好爬虫文件之后,输入命令 ``scrapy runspider --loglevel=INFO test.py``
settings
项目内使用settings命令,看到的settings.py里面的内容,项目外使用则是查看默认的scrapy设置 `` scrapy settings --get BOT_NAME `` 如果按照右图的项目设置,返回的回事test1,如果在项目外就是scrapybot
查询的时候需要带上要查询的字段信息,这些信息都可以打开项目的settings就可以看到
shell
shell经常在开发和调试的时候用到,此命令可以启动scrapy的交互命令终端 命令:调试百度,不打印日志 ``scrapy shell --nolog http://www.baidu.com `` 进入命令行之后,可以打印标题信息 ``ti=sel.xpath("/html/head/title") `` `` print(ti) `` 退出是``exit()``
startproject
前面已经介绍过,这个是用来创建项目,并生成一系列文件的
version
用于查看scrapy的版本 ``scrapy version`` 查看更多的版本信息 ``scraoy version -v``
view
此命令可以自动下载指定的网页并用浏览器打开,打开的时候是本地文件 ``scrapy view http://www.baidu.com``
项目命令
必须要在项目中运行才可以,必须要进入项目目录
常用
bench
此命令用于测试本地硬件,运行的时候会创建一个本地服务器,并以最大的速度爬行,可以从日志中看出爬取的速度 ``scrapy bench``
genspider
此命令可以用来生成一个基于scrapy模板的爬虫文件 查看有什么模板,可以任选一个创建 ``scrapy genspider -l`` 创建模板爬虫,-t后面跟模板名 爬虫名 域名 ``scrapy genspider -t basic test www.baidu.com`` 这样就创建了一个以basic为模板的名为test的爬取百度的爬虫如果想要查看模板的内容,用下面的命令 ``scrapy genspider -d basic``
check
此命令用来测试爬虫,爬虫名不带后缀的,采用合同(contract)的方式,这个不是很明白 ``scrapy check 爬虫名``
crawl
此命令用来启动爬虫,爬虫名不带后缀的 ``scrapy crawl 爬虫名``
list
在项目目录下输入,可以查看当前的所用的爬虫名称 ``scraoy list``
edit
用来编辑文件,windows下用的比较少,linux下用的比较多 ``scrapy edit 爬虫名``
parse
此命令可以实现获取指定的url,并指定相应的爬虫 这个参数很多,看书
实战:Items的编写
定义结构化数据
这是直接在items.py上改的,后面用得上 ``` import scrapy class MyfirstpjtItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() urlname=scrapy.Field() urlkey=scrapy.Field() urlcr=scrapy.Field()#版权 urladdr=scrapy.Field() ```
小练习
``` import scrapy #item是要定义一系列结构化的数据 class person(scrapy.Item): name=scrapy.Field() job=scrapy.Field() email=scrapy.Field() #创建一个实例 person1=person(name="weiwei",job="teacher",email="123456@qq.com") #可以看到,对应的数据以字典的形式储存 print(person1) #单独输出某字段的值 print("person1的name是"+person1["name"]) #需要改变某项的值 person1["name"]="leilei" print("person1的name修改后的值:"+person1["name"]) #查看所有的字段名 print("person1的所有字段名:"+str(person1.keys())) #查看对象的项目视图 print("person1的项目视图:"+str(person1.items())) ```
实战:Spider的编写
所有的爬虫必须继承spider这个类
这是利用genspider生成的爬虫文件first.py,可以看到,firstspider继承了Spider这个类 三个字段 name就是爬虫的名字 allowed_domains是允许爬行的域名 start_urls代表起始网址,里面可以定义多个url ``` import scrapy class FirstSpider(scrapy.Spider): name = 'first' allowed_domains = ['www.baidu.com'] start_urls = ['http://www.baidu.com/'] def parse(self, response): pass ``` parse方法没有特别指定,会采用默认方法处理response
修改生成的爬虫
```# -*- coding: utf-8 -*- import scrapy #调用目录文件下的itmes.py的MyfirstpjtItem类,这个在items实战里设置了 from Myfirstpjt.items import MyfirstpjtItem class FirstSpider(scrapy.Spider): name = 'first' allowed_domains = ['sina.com.cn'] #设置了三个网址,一个首页,两篇新闻 start_urls = [ 'http://news.sina.com.cn/', 'http://news.sina.com.cn/c/2018-07-04/doc-ihevauxk4488022.shtml', 'http://finance.sina.com.cn/china/gncj/2018-06-27/doc-iheqpwqx7497616.shtml' ] def parse(self, response): item=MyfirstpjtItem() item["urlname"]=response.xpath("/html/head/title/text()") print(item["urlname"]) ```
如果不使用start_urls
``` # -*- coding: utf-8 -*- import scrapy #调用目录文件下的itmes.py的MyfirstpjtItem类,这个在items实战里设置了 from Myfirstpjt.items import MyfirstpjtItem class FirstSpider(scrapy.Spider): name = 'first' allowed_domains = ['sina.com.cn'] #设置了三个网址,一个首页,两篇新闻 start_urls = [ 'http://news.sina.com.cn/', 'http://news.sina.com.cn/c/2018-07-04/doc-ihevauxk4488022.shtml', 'http://finance.sina.com.cn/china/gncj/2018-06-27/doc-iheqpwqx7497616.shtml' ] urls2={ "http://www.jd.com", "http://sina.com.cn", "http://www.baidu.com" } #这个是重新写了start_requests方法 def start_requests(self): for url in self.urls2: #调用默认的方法生成请求,这个yield会产生一个迭代器,是比较高级的用法 yield self.make_requests_from_url(url) def parse(self, response): item=MyfirstpjtItem() item["urlname"]=response.xpath("/html/head/title/text()") print(item["urlname"]) ```
XPath基础
/
选择标签
//
选择所有某一类标签
/text()
获取文本信息
//Z[@X="Y"]
获取所有属性为x的值为y的z标签
//img[class="f1"]
Spider类参数传递
修改Spider的爬虫文件
单个传递
命令:``scrapy crawl first -a myurl=http://www.sina.com.cn --nolog``就可以把网址传给爬虫文件 ``` # -*- coding: utf-8 -*- import scrapy #调用目录文件下的itmes.py的MyfirstpjtItem类,这个在items实战里设置了 from Myfirstpjt.items import MyfirstpjtItem class FirstSpider(scrapy.Spider): name = 'first' def __init__(self,myurl=None,*args,**kwargs): super(FirstSpider,self).__init__(*args,**kwargs) #输入要爬取的网址,对应之为接受到的参数 print("要爬取的网址为:%s"%myurl) #重新定义start_urls属性,属性值为传进来的参数值 self.start_urls=["%s"%myurl] def parse(self, response): item=MyfirstpjtItem() item["urlname"]=response.xpath("/html/head/title/text()") print("以下将显示爬取网址的标题") print(item["urlname"]) ```
多个传递
就是把单个传递的字符串用|分割,转化成列表传递给start_urls 命令: `` scrapy crawl first -a myurl="http://www.sina.com.cn|http://www.csdn.net --nolog"`` ``` # -*- coding: utf-8 -*- import scrapy #调用目录文件下的itmes.py的MyfirstpjtItem类,这个在items实战里设置了 from Myfirstpjt.items import MyfirstpjtItem class FirstSpider(scrapy.Spider): name = 'first' def __init__(self,myurl=None,*args,**kwargs): super(FirstSpider,self).__init__(*args,**kwargs) #用|号分割网址 myurllist=myurl.split("|") #遍历网址列表,分别输出 for i in myurllist: print("要爬取的网址为:%s"%i) self.start_urls=myurllist def parse(self, response): item=MyfirstpjtItem() item["urlname"]=response.xpath("/html/head/title/text()") print("以下将显示爬取网址的标题") print(item["urlname"]) ```
用XMLFeedSpider来分析XML源
复制xml网址(作者博客)
http://blog.sina.com.cn/rss/1615888477.xml
创建myxml爬虫项目
``scrapy startproject myxml``
编辑items.py
``` import scrapy class MyxmlItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() #存储文章标题 title=scrapy.Field() #存储对应链接 link=scrapy.Field() #存储对应文章作者 author=scrapy.Field() ```
创建一个爬虫文件
查看模板 ``scrapy genspider -l`` 使用xmlfeed模板 ``scrapy genspider -t xmlfeed myxmlspider sina.com.cn`` 修改myxmlspider.py ``` # -*- coding: utf-8 -*- from scrapy.spiders import XMLFeedSpider from myxml.items import MyxmlItem class MyxmlspiderSpider(XMLFeedSpider): name = 'myxmlspider' allowed_domains = ['sina.com.cn'] start_urls = ['http://blog.sina.com.cn/rss/1615888477.xml'] iterator = 'iternodes' # you can change this; see the docs #迭代开始节点rss itertag = 'rss' # change it accordingly def parse_node(self, response, node): i = MyxmlItem() #利用xpath表达式将对饮信息提取出来,并存储到对应的item中 #extract:返回选择器(列表)对应的节点的字符串(列表) i['title'] = node.xpath("/rss/channel/item/title/text()").extract() i['link'] = node.xpath("/rss/channel/item/link/text()").extract() i['author'] = node.xpath("/rss/channel/item/author/text()").extract() for j in range(len(i['title'])): print("第"+str(j+1)+"篇文章") print("标题是: "+i['title'][j]) print("对应链接是: "+i['link'][j]) print("对应的作者是: "+i['author'][j]) print("----------------") return i ```
运行爬虫
``scrapy crawl myxmlspider --nolog``
学会使用CSVFeedSpider
新建一个csv(作者有提供一个云文件)
http://yum.iqianyue.com/weisuenbook/pyspd/part12/mydata.csv
创建mycsv爬虫项目
``scrapy startproject mycsv``
编辑items.py
这个csv,我们要取姓名,性别,地址,email ``` import scrapy class MycsvItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() name=scrapy.Field() sex=scrapy.Field() addr=scrapy.Field() email=scrapy.Field() ```
创建一个爬虫文件
查看模板 ``scrapy genspider -l`` 使用csvfeed模板 ``scrapy genspider -t csvfeed mycsvspider iqianyue.com` 修改mycsvspider.py ``` # -*- coding: utf-8 -*- from scrapy.spiders import CSVFeedSpider from mycsv.items import MycsvItem class MycsvspiderSpider(CSVFeedSpider): name = 'mycsvspider' allowed_domains = ['iqianyue.com'] #定义csv所在的网址 start_urls = ['http://yum.iqianyue.com/weisuenbook/pyspd/part12/mydata.csv'] #定义csv的头部 headers = ['name', 'sex', 'addr', 'email'] #定义数据分割符号,csv是以逗号玮分隔符 delimiter = ',' # Do any adaptations you need here #def adapt_response(self, response): # return response def parse_row(self, response, row): i = MycsvItem() #提取信息,书里用到了encode,实际上不用,提示需要的是一个字符串,而不是字节 i['name'] = row['name'] i['sex'] = row['sex'] i['addr'] = row['addr'] i['email'] = row['email'] #打印信息,为了好看,打印信息放成一行 print("名字是:"+i['name']+" 性别是:"+i['sex']+" 地址是:"+i['addr']+" 邮箱是:"+i['email']) print('-----------------') return i ```
运行爬虫
``scrapy crawl mycsvspider --nolog``
Scrapy爬虫多开技能
CrawProcess
<a href="https://doc.scrapy.org/en/latest/topics/practices.html" target="_blank">官方文档</a>
修改craw源码+自定义命令
创建mymultispd项目
``scrapy startproject mymultispd``
创建三个爬虫文件
先进入项目 ``cd mymultispd`` 再创建三个基本模板的爬虫文件 ``scrapy genspider -t basic myspd1 sina.com.cn`` ``scrapy genspider -t basic myspd2 sina.com.cn`` ``scrapy genspider -t basic myspd3 sina.com.cn``
复制scrapy项目的crawl源码,并修改
scrapy项目地址
``https://github.com/scrapy/scrapy``
crawl源码
crawl的地址是: ``https://github.com/scrapy/scrapy/blob/master/scrapy/commands/crawl.py`` ``` import os from scrapy.commands import ScrapyCommand from scrapy.utils.conf import arglist_to_dict from scrapy.utils.python import without_none_values from scrapy.exceptions import UsageError class Command(ScrapyCommand): requires_project = True def syntax(self): return "[options] <spider>" def short_desc(self): return "Run a spider" def add_options(self, parser): ScrapyCommand.add_options(self, parser) parser.add_option("-a", dest="spargs", action="append", default=[], metavar="NAME=VALUE", help="set spider argument (may be repeated)") parser.add_option("-o", "--output", metavar="FILE", help="dump scraped items into FILE (use - for stdout)") parser.add_option("-t", "--output-format", metavar="FORMAT", help="format to use for dumping items with -o") def process_options(self, args, opts): ScrapyCommand.process_options(self, args, opts) try: opts.spargs = arglist_to_dict(opts.spargs) except ValueError: raise UsageError("Invalid -a value, use -a NAME=VALUE", print_help=False) if opts.output: if opts.output == '-': self.settings.set('FEED_URI', 'stdout:', priority='cmdline') else: self.settings.set('FEED_URI', opts.output, priority='cmdline') feed_exporters = without_none_values( self.settings.getwithbase('FEED_EXPORTERS')) valid_output_formats = feed_exporters.keys() if not opts.output_format: opts.output_format = os.path.splitext(opts.output)[1].replace(".", "") if opts.output_format not in valid_output_formats: raise UsageError("Unrecognized output format '%s', set one" " using the '-t' switch or as a file extension" " from the supported list %s" % (opts.output_format, tuple(valid_output_formats))) self.settings.set('FEED_FORMAT', opts.output_format, priority='cmdline') def run(self, args, opts): if len(args) < 1: raise UsageError() elif len(args) > 1: raise UsageError("running 'scrapy crawl' with more than one spider is no longer supported") spname = args[0] self.crawler_process.crawl(spname, **opts.spargs) self.crawler_process.start() if self.crawler_process.bootstrap_failed: self.exitcode = 1 ```
创建一个文件夹,放改造的源码(spider同级目录)
文件夹名称随意,这里命名为mycmd,这里都是通过cmd命令创建,也可以直接手动建立 ``mkdir mycmd`` 进入文件夹 ``cd mycmd`` 创建文件 ``echo # >mycrawl.py``
改造crawl源码
打开mycrawl.py复制crawl的源码,并修改 主要修改的部分为,run方法,其他的地方都没有变 ``` #这里是修改的主要部分,可以对比查看源码改了哪些地方 def run(self, args, opts): #获取爬虫列表,这里牵涉到一个问题,从哪里获取列表 spd_loader_list=self.crawler_process.spider_loader.list() #遍历爬虫 for spname in spd_loader_list: #下面的代码运行爬虫 self.crawler_process.crawl(spname, **opts.spargs) print("此时启动的爬虫为: "+spname) self.crawler_process.start() if self.crawler_process.bootstrap_failed: self.exitcode = 1 ```
增加__init__文件
只是确保有这个文件 ``echo #>__init__.py``
修改settings.py
原来的是 ``` BOT_NAME = 'mymultispd' SPIDER_MODULES = ['mymultispd.spiders'] NEWSPIDER_MODULE = 'mymultispd.spiders' ROBOTSTXT_OBEY = True ``` 增加一条 ``COMMANDS_MODULE='mymultispd.mycmd'`` ``` BOT_NAME = 'mymultispd' SPIDER_MODULES = ['mymultispd.spiders'] NEWSPIDER_MODULE = 'mymultispd.spiders' COMMANDS_MODULE='mymultispd.mycmd' ROBOTSTXT_OBEY = True ```
查看命令
在前面的步骤都完成之后,可输入命令行``scrapy -h``查看,如果配置没有问题的话,命令中应该会出现``mycrawl``
运行爬虫
不需要输入文件名直接运行 ``scrapy mycrawl --nolog``
避免被禁止
碰到反爬虫机制,会被禁止
策略
禁止cookie
通过禁用本地cookie,让对方无法识别我们的会话信息 在settings.py中,有一段代码是被注视的 ``` #COOKIES_ENABLED = False ``` 只需要把注释取消就可以禁用本地cookie
设置下载延时
对于会检查访问频率的网站,可以设置下载延时 将settings.py中的注释取消,并设置具体的值,单位是秒 ``` #DOWNLOAD_DELAY = 3 ```
设置代理ip池
创建一个下载中间件middlewares
事实上,项目创建会自动生成一个 middlewares.py,可以直接在上面修改
settings.py设置ip池
``` #设置ip池 IPPOOL=[ {"ipaddr":"121.16.204.179:8118"}, {"ipaddr":"14.118.254.113:6666"}, {"ipaddr":"118.190.95.43:9001"}, ] ```
修改middlewares.py
``` from scrapy import signals import random from myippoolspider.settings import IPPOOL #导入与代理服务器相关的中间件类 from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware #可以自己定义一个类,也可以直接在下面修改类(xxspiderDownloaderMiddleware),这里自己创建一个 class IpPools(HttpProxyMiddleware): def __init__(self,ip=""): self.ip=ip def process_request(self,request,spider): #先随机选择一个ip thisip=random.choice(IPPOOL) print("当前使用的ip是"+thisip['ipaddr']) #将对应的ip实际添加为具体的代理,用该ip进行爬取 request.meta["proxy"]="http://"+thisip["ipaddr"] ```
settings.py绑定中间件
找到settings文件中的DOWNLOADER_MIDDLEWARES这个字段,把注释取消 ``` # DOWNLOADER_MIDDLEWARES = { # 'myippoolspider.middlewares.MyippoolspiderSpiderMiddleware': 543, # } ``` 增加一段代码,字典里的值是中间件的顺序,这个中间件顺序需要深入了解一下 ``` DOWNLOADER_MIDDLEWARES = { # 'myippoolspider.middlewares.MyippoolspiderDownloaderMiddleware': 543, 'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware':123, 'myippoolspider.middlewares.IpPools':125 } ```
设置好items和爬虫文件,就可以运行了
设置用户代理池
同样创建一个中间件
settings.py设置ua池
``` #设置ua池 user_agent_pools=[ '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.1063.0 Safari/536.3', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36', ] ```
修改middlewares.py
``` from scrapy import signals import random from myippoolspider.settings import user_agent_pools #导入与代理浏览器相关的中间件类 from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware #可以自己定义一个类,也可以直接在下面修改类(xxspiderDownloaderMiddleware),这里自己创建一个 class UserAgent(UserAgentMiddleware): def __init__(self,ua=""): self.user_agent=ua def process_request(self,request,spider): #先随机选择一个ua thisua=random.choice(user_agent_pools) print("当前使用的user-agent是"+thisua) #设置ua request.headers.setdefault('User-Agent',thisua) ```
settings.py绑定中间件
``` DOWNLOADER_MIDDLEWARES = { # 'myippoolspider.middlewares.MyippoolspiderDownloaderMiddleware': 543, 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware':2, 'myippoolspider.middlewares.UserAgent':1 } ```
设置好items和爬虫文件,就可以运行了
其他方法
谷歌cache
分布式爬行
Scrapy核心架构
Scrapy架构
scrapy引擎
是整个scrapy架构的核心,负责控制整个数据处理
调度器
实现存储待爬取的网址,并确定这些网址的优先级,决定下一次爬取哪一个地址
下载器
实现对网络上要爬取的网页资源进行高速下载
下载中间件
处于下载器和scrapy引擎之间的一个特定的组件,用于下载器和scrapy引擎之间的通信进行处理
蜘蛛(也叫爬虫))
主要负责接收scrapy引擎中个response响应
爬虫中间件
用于对爬虫组件和scrapy引擎之间的通信处理
实体管道
接受从蜘蛛组件中提取出来的项目(item)
Scrapy工作流
Scrapy中文输出与存储
Scrapy的中文输出
scrapy的中文输出已经比较完善,在python2.x中需要用encode编码,而3.x可以直接输出
Scrapy的中文存储
settings配置pipelines
取消注释 ``` ITEM_PIPELINES = { 'Myfirstpjt.pipelines.MyfirstpjtPipeline': 300, } ```
编写pipelinse.py
需要注意的是,爬虫文件里的item必须要返回,管道文件才能接受得到 ``` import codecs class MyfirstpjtPipeline(object): def __init__(self): self.file=codecs.open("test.txt","wb",encoding="utf-8") def process_item(self, item, spider): #设置每行要写的内容 l=str(item)+"\n" #打印输出信息 self.file.write(l) return item def close_spider(self,spider): #关闭文件 self.file.close() ```
运行爬虫就可以看到结果
输出中文到JSON文件
与上面一样,只是管道文件需要做些许修改 ``` import codecs import json class MyfirstpjtPipeline(object): def __init__(self): self.file=codecs.open("test.json","wb",encoding="utf-8") def process_item(self, item, spider): #设置每行要写的内容 #通过dic(item)将item转化成字典 #通过json模块下的dumps()序列化数据 #ensure_ascii=False让写到json的数据不使用编码ascii,从而显示成中文 i=json.dumps(dict(item),ensure_ascii=False) #得到的数据后加上"\n"换行符形成要写入的数据 line=i+'\n' print(line) #打印输出信息 self.file.write(line) return item def close_spider(self,spider): #关闭文件 self.file.close() ```
编写自动爬取网页的爬虫(当当网某频道)
创建autopjt项目
``scrapy startproject autopjt``
实战:items的编写
这里准备爬取当当网图书频道,定义四个字段,name,price,link,comnum ``` import scrapy class AutopjtItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() #定义图书名称 name=scrapy.Field() #定义图书价格 price=scrapy.Field() #定义图书链接 link=scrapy.Field() #定义图书评论数量 comnum=scrapy.Field() ```
实战:pipelines的编写
``` import codecs import json class AutopjtPipeline(object): def __init__(self): self.file=codecs.open("book.json","wb",encoding="utf-8") def process_item(self, item, spider): #设置每行要写的内容 print(len(item["comnum"])) for i in range(len(item['name'])): name=item['name'][i] price=item['price'][i] link=item['link'][i] comnum=item['comnum'][i] #把字段信息放到一个新的字典里面,这样打开文件的时候就会好看很多 goods={"name":name,"price":price,"link":link,"commum":comnum} #序列化字典,ensure_ascii=False让写到json的数据不使用编码ascii,从而显示成中文,indent=1代表1个缩进 i=json.dumps(goods,ensure_ascii=False,indent=1) #打印输出信息 self.file.write(i+"\n") return item def close_spider(self,spider): #关闭文件 self.file.close() ```
实战:settings的编写
将注释取消 ``` ITEM_PIPELINES = { 'autopjt.pipelines.AutopjtPipeline': 300, } ```
自动爬虫编写实战
创建一个basic模板的爬虫文件 ``scrapy genspider -t basic autospd dangdang.com`` 改写autospd.py ``` import scrapy from autopjt.items import AutopjtItem from scrapy.http import Request class AutospdSpider(scrapy.Spider): name = 'autospd' allowed_domains = ['dangdang.com'] start_urls = ['http://search.dangdang.com/?key=python&act=input&show=big&page_index=1'] def parse(self, response): item=AutopjtItem() item['name']=response.xpath("//a[@class='pic']/@title").extract() item['price']=response.xpath("//span[@class='price_n']/text()").extract() item['link']=response.xpath("//a[@class='pic']/@href").extract() item['comnum']=response.xpath("//a[@name='itemlist-review']/text()").extract() #提取完后返回item yield item #通过循环自动爬取,这里只设置了一页,可以自己设置 for i in range(2,3): #通过上面的网址构建要爬取的格式 url="http://search.dangdang.com/?key=python&act=input&show=big&page_index="+str(i) yield Request(url,callback=self.parse) ```
调试与运行
调试的时候如果出现了forbidden for robot.txt,这个是因为遵循机器人协议,打开settings.py找到 ``` # Obey robots.txt rules ROBOTSTXT_OBEY = True ``` 可以看到这个是true,这个就代表我们这个爬虫是遵循机器人协议的,只需要改成False就可以无视机器人协议
CrawlSpider
初识CrawlSpider
scrapy框架的自带的自动爬取网页的爬虫
这个是生成的craw模板文件 ``` import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule class CwspdSpider(CrawlSpider): name = 'cwspd' allowed_domains = ['sohu.com'] start_urls = ['http://sohu.com/'] rules = ( Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True), ) def parse_item(self, response): i = {} #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract() #i['name'] = response.xpath('//div[@id="name"]').extract() #i['description'] = response.xpath('//div[@id="description"]').extract() return i ``` ```
链接提取器(linkExtractor)
负责将response中的网址提取出来,这个在下面的实战中可以看得出来
参数
allow
提取符合正则表达式的链接
deny
不提取符合正则表达式的链接
restrict_xpaths
使用xpath表达式,和allow共同作用过滤链接
allow_domain
允许提取的域名
deny_domain
不想提取的域名
实战:CrawlSpider实例(新浪新闻)
创建项目
因为搜狐新闻的网址发生了很大的变化,不再如书中所说,换成新浪的 ``scrapy startproject mycwpjt``
编辑items.py
只取新闻标题和链接 ``` import scrapy class MycwpjtItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title=scrapy.Field() link=scrapy.Field() ```
编辑pipelines.py
为了显示效果,只做简单的打印 ``` class MycwpjtPipeline(object): def process_item(self, item, spider): #判断列表成员数,如果大于零才打印 if(len(item['title']))>0: print(item["title"]) if(len(item['link']))>0: print(item["link"]) print("----------------------") return item ```
编辑settings.py
将注释取消 ``` ITEM_PIPELINES = { 'autopjt.pipelines.AutopjtPipeline': 300, } ```
编辑爬虫文件
``` import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from mycwpjt.items import MycwpjtItem class CwspdSpider(CrawlSpider): name = 'cwspd' allowed_domains = ['sina.com.cn'] start_urls = ['http://news.sina.com.cn/'] rules = ( #新浪新闻网的网页是这种格式 #http://news.sina.com.cn/c/2018-07-05/doc-ihexfcvk0808111.shtml Rule(LinkExtractor(allow=('.*?/doc.*?.shtml'),allow_domains="sina.com.cn"),callback='parse_item', follow=True), ) def parse_item(self, response): i = MycwpjtItem() i['title'] = response.xpath('/html/head/title/text()').extract() i['link'] = response.xpath("/html/head/meta[@property='og:url']/@content").extract() return i ```
运行爬虫
``scrapy crawl cwspd --nolog``
Scrapy高级应用
如何在Python3中操作数据库
准备
phpstud集成环境
pymysql模块
pymysql
连接,执行语句
连接名叫mypydb的本地mysql数据库 ``` import pymysql con1=pymysql.connect(host="127.0.0.1",user="root",passwd="root",db="mypydb") ``` 如果没有mypydb数据库,可以创建一个 ``con1.query("CREATE DATABASE mypydb")`` 再使用这个数据库 ``con1.query("USE mypydb")`` 创建一个数据表 `` con1.query("CREATE TABLE mytb(title VARCHAR(20)) NOT NULL,keyword VARCHAR(30) ") `` 插入一些数据,用query语句 ``con1.query()``
select查询
创建游标 ``cursor=conn.cursor()`` 执行语句 ``cursor.excute("select * from mytb")`` 取游标值 ``` for i in cursor: print("当前查询的是"+str(cursor.rownumber)+"行") print(i[0]) print(i[1]) 等等 ```
爬取内容写进MySQL
创建项目mysqlpjt
生成crawlspider文件
``scrapy genspider -t crawl mysql sina.com.cn``
编辑items.py
``` import scrapy class MysqlpjtItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() #网页标题 name=scrapy.Field() #keywd存储网页 keywd=scrapy.Field() ```
编辑pipelines.py
为了显示效果,只做简单的打印 ``` import pymysql class MysqlpjtPipeline(object): def __init__(self): self.conn=pymysql.connect(host="127.0.0.1",user="root",passwd="root",db="mypydb") def process_item(self, item, spider): name=item["name"][0] key=item["keywd"][0] sql="insert into mytb(title,keyword) VALUES ('"+name+"','"+key+"')" print(sql) self.conn.query(sql) return item def close_spider(self,spider): self.conn.close() ```
编辑settings.py
将注释取消 ``` ITEM_PIPELINES = { 'mysqlpjt.pipelines.MysqlpjtPipeline': 300, } ```
编辑爬虫文件
``` import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from mysqlpjt.items import MysqlpjtItem class MysqlSpider(CrawlSpider): name = 'mysql' allowed_domains = ['sina.com.cn'] start_urls = ['http://news.sina.com'] rules = ( Rule(LinkExtractor(allow=('.*?/doc.*?.shtml'),allow_domains="sina.com.cn"),callback='parse_item', follow=True), ) def parse_item(self, response): i = MysqlpjtItem() i['name'] = response.xpath('/html/head/title/text()').extract() print(i["name"]) i['keywd'] = response.xpath('/html/head/meta[@name="keywords"]/@content').extract() return i ```
项目实战篇
博客类爬虫项目(和讯博客)
数据库的设计
创建一个数据库名为hexun ``create table hexun`` 使用数据库 ``use hexun`` 创建一个数据表名为myhexun ``` create table myhexun(id int(10) auto_increment primary key not null,name varchar(30),url varchar(100),hits int(15),comment int (15)); ```
创建一个项目hexunpjt
生成一个basic模板的爬虫文件
编辑items.py
``` import scrapy class HexunpjtItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() name=scrapy.Field() url=scrapy.Field() hits=scrapy.Field() comment=scrapy.Field() ```
编辑pipelines.py
``` import pymysql class HexunpjtPipeline(object): def __init__(self): self.conn=pymysql.connect(host="127.0.0.1",user="root",password="root",db="hexun") def process_item(self, item, spider): for j in range(0,len(item['name'])): name=item['name'][j] url=item['url'][j] hits=item['hits'][j] comment=item['comment'][j] hits=item['hits'][j] sql="insert into myhexun(name,url,hits,comment) VALUES ('"+name+"','"+url+"','"+hits+"','"+comment+"')" self.conn.query(sql) return item def close_spider(self,spider): self.conn.close() ```
编辑settings.py
修改值为false ``` ROBOTSTXT_OBEY = False ``` 取消注释 ``` COOKIES_ENABLED = False ``` ``` 取消注释 ITEM_PIPELINES = { 'hexunpjt.pipelines.HexunpjtPipeline': 300, } ```
编辑爬虫文件
``` # -*- coding: utf-8 -*- import scrapy import re import urllib.request from hexunpjt.items import HexunpjtItem; from scrapy.http import Request class HexunspdSpider(scrapy.Spider): name = 'hexunspd' allowed_domains = ['hexun.com'] uid="14755952" def start_requests(self): #博客格式 #http://14755952.blog.hexun.com/p2/default.html yield Request("http://"+str(self.uid)+".blog.hexun.com/p1/default.html",headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"}) def parse(self, response): item=HexunpjtItem() item['name']=response.xpath('//span[@class="ArticleTitleText"]/a/text()').extract() item['url']=response.xpath('//span[@class="ArticleTitleText"]/a/@href').extract() #接下来使用urllib和re模块获取博文的评论数和阅读数 #首先提取存储评论数和点击数网址的正则 pat1='<script type="text/javascript" src="(http://click.tool.hexun.com/.*?)">' #提取网址 hcurl=re.compile(pat1).findall(str(response.body))[0] header2=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36") opener=urllib.request.build_opener() opener.addheaders=[header2] urllib.request.install_opener(opener) #提取评论和点击 data2=urllib.request.urlopen(hcurl).read() pat2="'click\d*?','(\d*?)'" pat3="'comment\d*?','(\d*?)'" item['hits']=re.compile(pat2).findall(str(data2)) item['comment']=re.compile(pat3).findall(str(data2)) yield item #提取博文列表页的总页数 pat4="blog.hexun.com/p(.*?)/" #获取的列表中,倒数第二个是总页数 data2=re.compile(pat4).findall(str(response.body)) if(len(data2)>=2): totalurl=data2[-2] else: totalurl="1" for i in range(2,int(totalurl)+1): nexturl="http://"+str(self.uid)+".blog.hexun.com/p"+str(i)+"/default.html" yield Request(nexturl,headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"}) ```
图片类爬虫项目(千图网)
创建项目qtpjt
生成一个basic模板的爬虫文件
编辑items.py
``` import scrapy class QtpjtItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() #建立图片存储的网址 picurl=scrapy.Field() #简历春初图片网址中的图片名,方便构造本地文件名 picid=scrapy.Field() ```
编辑pipelines.py
``` import os import urllib.request class QtpjtPipeline(object): def process_item(self, item, spider): for i in range(0,len(item['picurl'])): if os.path.exists("pic")==False: os.mkdir("pic") localpath="pic\\"+item['picid'][i]+".jpg" print(localpath) urllib.request.urlretrieve(item['picurl'][i],filename=localpath) return item ```
编辑settings.py
修改值为false ``` ROBOTSTXT_OBEY = False ``` 取消注释 ``` COOKIES_ENABLED = False ``` 取消注释 ``` ITEM_PIPELINES = { 'qtpjt.pipelines.QtpjtPipeline': 300, } ```
编辑爬虫文件
``` # -*- coding: utf-8 -*- # -*- coding: utf-8 -*- import scrapy import re from qtpjt.items import QtpjtItem from scrapy.http import Request class QtpicspdSpider(scrapy.Spider): name = 'qtpicspd' allowed_domains = ['58pic.com'] #网址是淘宝素材的热门下载第一页 start_urls = ['http://www.58pic.com/tupian/taobao-0-0-id-0-0-%E6%B7%98%E5%AE%9D-0_2_0_0_0_0_0-01.html'] def parse(self, response): item=QtpjtItem() paturl='(http://pic.qiantucdn.com/58pic/.*?.jpg)' item['picurl']=re.compile(paturl).findall(str(response.body)) print(item['picurl']) patlocal='http://pic.qiantucdn.com/58pic/.*?/.*?/.*?/(.*?).jpg' item['picid']=re.compile(patlocal).findall(str(response.body)) yield item for i in range (2,3): nexturl="http://www.58pic.com/tupian/taobao-0-0-id-0-0-%E6%B7%98%E5%AE%9D-0_2_0_0_0_0_0-0"+str(i)+".html" yield Request(nexturl,callback=self.parse) ```
模拟登录爬虫项目
验证码识别三种方式
手动打码
平台打码
自己编写自动识别程序
模拟登录爬虫项目(豆瓣)
创建项目loginpjt
这个例子只编辑了一个爬虫文件,没有用到items,和pipeline这些
``` # -*- coding: utf-8 -*- import scrapy import urllib.request from scrapy.http import Request,FormRequest class LoginspdSpider(scrapy.Spider): name = 'loginspd' allowed_domains = ['douban.com'] header={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"} def start_requests(self): return [FormRequest("https://accounts.douban.com/login",headers=self.header,meta={"cookiejar":1},callback=self.parse)] def parse(self,response): captcha=response.xpath('//img[@id="captcha_image"]/@src').extract() if len(captcha)>0: print("此时有验证码") urllib.request.urlretrieve(captcha[0],filename="captcha.png") print("请输入验证码") captcha_value=input() data={ "form_email":"302880911@qq.com", "form_password":"qq19930813", "captcha-solution":captcha_value, "redir":"https://www.douban.com/people/103071823/" } else: data={ "form_email":"302880911@qq.com", "form_password":"*******", "redir":"https://www.douban.com/people/103071823/" } return (FormRequest.from_response(response, #设置cookie信息 headers=self.header, formdata=data, callback=self.next, )) def next(self,response): with open("test.txt","wb") as file: file.write(response.body) print("此时已完成登录,并爬取个人数据") #网页标题xpath xtitle="/html/head/title/text()" #日记标题xpath xnotetitle="//div[@class='note-header pl2']/a/@title" #日记发表时间 xnotetime="//div[@class='note-header pl2']//span[@class='pl']/text()" #日记发表内容 xnotecontent="//div[@class='mbtr2']/div[@class='note']/text()" #日记连接 xnoteurl="//div[@class='note-header pl2']/a/@href" title=response.xpath(xtitle).extract() notetitle=response.xpath(xnotetitle).extract() notetime=response.xpath(xnotetime).extract() notecontent=response.xpath(xnotecontent).extract() noteurl=response.xpath(xnoteurl).extract() print("网页标题是"+title[0]) for i in range(0,len(notetitle)): print("第"+str(i)+"篇文章的信息如下:") print("文章标题为:"+notetitle[i]) print("文章发表时间为:"+notetime[i]) print("文章内容为:"+notecontent[i]) print("文章连接为:"+noteurl[i]) ```
调试运行
豆瓣出现403错误,就在settings.py里面把USER-AGENT注释取消,并添加相应的浏览器信息