1、实例化采集类后,自带一些header信息,类似user-agent、accept之类的,能不手动添加就不手动添加(已实现)

2、在执行了采集后,获取采集到的响应头,解析其中的数据,该记录的记录该执行的执行,在下次调用采集方法时继承获取到的信息(已实现)

3、可以采集纯文本内容,也可以采集二进制流,方便采集页面和下载相关文档(已实现)

4、支持不同的字符编码,响应编码,比如gbk、utf8等,比如gzip、deflate等(已实现)

5、支持不同的请求方法,比如get、put、post、delete、head等(已实现)

6、不管是否采集异常,都能返回状态码(已实现)

7、可以伪造、添加各种头信息,伪造、添加cookie之类的信息,类似 Oauth:xxxx,Signture:xxx等

8、支持301、302之类的自动跳转采集,支持meta自动跳转采集

9、自动完成网址补全,不需要我们在根据采集目标,提取到链接后自己再计算

10、如果可以,尽量支持异步采集

11、如果可以,尽量支持事件委托

12、如果可以,尽量支持proxy

13、如果可以,尽量支持断点续传下载和上传

前一片文章我们建立了一个自定义的爬虫类,已经实现了部分需求了,本文将继续实现剩余的需求。在继续修改我们的类之前,先谈谈spyder。

-----------------------------

为什么要先聊聊spyder呢,主要是因为,老顾找不到一个完整的python包的使用手册,资料都是太零碎的东西,整理起来很麻烦,没有像php手册,或者msdn命名空间介绍一样的东西。就像之前几篇文章一样,每次想做的什么,都要先百度半天,结果本系列文章极度难产。那么spyder有什么好聊的呢?我们来看看。。。

打开spyder,如界面所示,他分成了几个不同的区域,通常,我们都在左边输入py代码,右下可以看输出结果,右上。。。没好好利用起来啊

比如左侧,我们输入一段代码

[n for n in range(1,100)]

选中并执行这段代码(F9),右下控制台就可以出现反馈信息

这是一个很方便的调试方法了,当代码中存在交互指令,例如input的时候,右下控制台可以进行交互操作,录入一些信息,这个就不多说了,大家都会用

然后,来想办法利用上右上区域,来辅助我们的学习和工作

在代码区域输入代码

import requests
req = requests()

然后将鼠标指向 requests()这个方法,界面发生变动了

当我们在浮动的这个提示上点一下后,右上区域就发生了内容改变,Help页里出现了好多信息

嗯。。。这就很方便了,可以查看方法的具体使用方式了,可惜的是,他并没有像vs里一样,将类的方法全部列出,只能靠类.,然后等待spyder响应后,出现可以使用的属性或方法,然后去选择我们可能需要的内容

现在,我们把指令修改下

import requests
req = requests.Request(url='https://www.baidu.com',method='GET')

运行这些代码,将右上的区域切换到变量资源管理器里(Variable Explorer)

我们在左侧运行的代码所产生的变量,都会在这里列出,只要没有清除,关闭,一直就保留在这里了,比如刚才的req,我们就可以看到的他描述,用鼠标双击这个变量看看

这个就是一个比较完整的实例属性和方法了,他里面将这个实例所有可用的方法和属性列出,并可展开查看,这样,就能避免之前我们找不到方法的问题了,直接给一个变量赋值成该类,然后到这边查看,是不是可以代替了vs里的对象浏览器了。

嗯,就简单的说两句spyder,毕竟老顾学python完全是自己摸索,而且岁数大了,看其他视频感觉很不适应,所以就慢慢来吧。至于其他的python 相关的IDE有没有提供这样的查阅方式,老顾也不了解,刚转行的同学们可以自行摸索。然后,我们回归主题,继续搞我们的爬虫类。

------------------------------------------------

7、可以伪造、添加各种头信息,伪造、添加cookie之类的信息,类似 Oauth:xxxx,Signture:xxx等

一般来说,同一个站点很少有需要频繁改动头信息,cookie信息的地方,反爬的网站咱们另说,等进入采集实战的时候再进行应对,咱先说普通的网站。所以,实例化一次,并设置好这些信息,基本上这个实例就可以进行整站采集了,线程问题以后再说。

那么,我们这次把目光瞄准到天眼查这个站,试着根据他的响应,来调整我们的代码,使之能伪造cookie,接收header

先来次没调整前的采集

from spider import Ajaxajax = Ajax()
html = ajax.Http('https://www.tianyancha.com',Ajax.Method.GET)
print(ajax.status,html)

很好,可以正常采集天眼查首页,但我们知道,天眼查会返回一些cookie信息,我们现在并没有接收,所以,进行下查找,看看cookie到底存放在哪里

因为对各种包都不熟悉,所以先在爬虫类里面添加两个属性

 @propertydef ResposeHeader(self):return self.__headers@propertydef Session(self):return self.__session

一个是用来返回响应头信息的,一个是用来返回会话信息的,让我们看看

好家伙。。。响应头居然有这么多数据么?548个?哦,展开一看,没那么多,548字节啊,吓我一大跳。

把响应头信息粘贴出来,整理一下看看

{
'Date': 'Wed, 30 Jun 2021 02:25:43 GMT',
'Content-Type': 'text/html; charset=utf-8',
'Transfer-Encoding': 'chunked',
'Connection': 'keep-alive',
'Set-Cookie': 'aliyungf_tc=8e44b1cb0fc5f37d29864918aa197ec6ed802b989655bf8732efdcd291861558; Path=/; HttpOnly, acw_tc=76b20f8c16250199433016427e4b75bd21ba7840934a083d22e010ebf1aedd;path=/;HttpOnly;Max-Age=1800, csrfToken=s4i4TN-WIKaLgWAXW3qHwbK5; path=/; secure, TYCID=784de430d94a11eb8216f7b2b73bb5b3; path=/; expires=Fri, 30 Jun 2023 02:25:43 GMT; domain=.tianyancha.com',
'Content-Encoding': 'gzip'
}

哦吼,发现第一个关键信息,Set-Cookie,这个就是服务器发给浏览器的cookie信息了,Set-Cookie是其中的一种方式,记下,一会处理

然后,再看看会话里都有什么

很显然,会话里也保持了cookies的信息,里面有4个cookie了

print(ajax.Session.cookies)<RequestsCookieJar[<Cookie TYCID=784de430d94a11eb8216f7b2b73bb5b3 for .tianyancha.com/>, <Cookie acw_tc=76b20f8c16250199433016427e4b75bd21ba7840934a083d22e010ebf1aedd for www.tianyancha.com/>, <Cookie aliyungf_tc=8e44b1cb0fc5f37d29864918aa197ec6ed802b989655bf8732efdcd291861558 for www.tianyancha.com/>, <Cookie csrfToken=s4i4TN-WIKaLgWAXW3qHwbK5 for www.tianyancha.com/>]>

得到了一个不知道什么对象的列表,先不管,总之,这里也有cookies,所以,现在我们需要自己定义一个cookies变量,存储这些信息,并在下次采集时继承进来,嗯,也得支持从外边添加cookie。另外,老顾注意到了这个cookie里的domain问题,伪造cookie,domain信息也很重要哦,参考老顾的另一个文章https://blog.csdn.net/superwfei/article/details/9198283?spm=1001.2014.3001.5502,部分网站对cookie验证时,会更严格一些,对不再特定domian的cookie是不予承认的,期待python的cookie处理。。。。

在__init__里,追加一个属性

self.cookies = requests.utils.cookiejar_from_dict({})

然后,我们就可以通过实例添加cookie了

import re
from spider import Ajaxajax = Ajax()
# 为了获取初始cookie,先访问下天眼查首页
ajax.Http('https://www.tianyancha.com')
ajax.cookies.set('tyc-user-info', '{***********}', domain='.tianyancha.com')
ajax.cookies.set('auth_token', '****************', domain='.tianyancha.com')
html = ajax.Http('https://www.tianyancha.com/company/2968548568',Ajax.Method.GET)
# 显示现在已有cookies
print(ajax.cookies)
print(re.findall(r'<div class="detail ">[\s\S]*?(?=<div class="card-tag company-header-card">)',html,re.I))

很好,cookie伪造成功了,对于域名带前缀点的问题直接就不是问题,证据就是,他没提示登录,其次,电话号码没有星号隐藏。

在这段代码中,我们使用了两次Http方法,第一次是获取初始cookie,如果没有初始cookie,那么我们就需要通过cookies.set方法,自己添加初始cookie,老顾懒得添加,让他自动获取好了,然后第二次采集前,我们追加了两个cookie,并继承了第一次采集的cookie,所以,正确得到了我们的期望结果,而cookie在同一实例内,只需要添加一次,我们这个ajax实例再次使用Http访问天眼查其他企业信息时,就不需要再关注cookie信息了。

剩下的就是伪造请求头了,之前,我们的Header定义,是一个固定的词典,现在要对他进行改装一下,变成动态的词典,同样,在__init__里追加两个赋值

     self.__requestHeaders = {}self.__refreshRequestHeaders()

调整Header属性的实现

 @propertydef Header(self):return self.__requestHeaders

然后,增加一个私有方法,用来初始化请求头信息

 def __refreshRequestHeaders(self):self.__requestHeaders.update({'refer':self.refer,'user-agent':self.agent,'accept':self.accept,'accept-encoding':self.encoding,'accept-language':self.lang,'cache-control':self.cache})

最后,我们给Ajax类增加一个公开的方法,AddHeader,用来将信息添加到请求头中

 def AddHeader(self,key:str = None,val:str = None,dic:dict = None):if dic != None and isinstance(dic,dict):self.__requestHeaders.update(dic)if key != None and val != None:self.__requestHeaders.update({key:val})
from spider import Ajaxajax = Ajax()
ajax.AddHeader(dic={'oauth':'userinfo'})
ajax.AddHeader('pwd','***')
print(ajax.Header)

运行一下,看看结果

很好,请求头信息已更新了,虽然有时候需要删除一些请求头,我这里就不去实现了,大家有需求的话,自行实现就可以了,那么第七个需求也告一段落了,下边开始处理跳转问题。

8、支持301、302之类的自动跳转采集,支持meta自动跳转采集

让我们找一个带跳转的网址,比如:http://m6z.cn/6uVNKg,一个用短链接生成器生成的地址

来,我们尝试下,这个请求会发生什么

from spider import Ajax
ajax = Ajax()
html = ajax.Http('http://m6z.cn/6uVNKg')
print(ajax.status,ajax.ResposeHeader)
print(html)

他自动跳过去了!返回的状态码也是200!中间301、302的过程省略掉了!https://blog.csdn.net/qq_36145663/article/details/101703090,原来,你不想自动301、302,还得设置这个参数allow_redirects=False,算了,让他自动跳吧,不过,我们还是加一个开关,可以用来关闭这个自动跳转,在__init__里追加一个属性

self.redirect = True

在发送请求位置修改 send 参数

res = self.__session.send(request=pre,allow_redirects=self.redirect)

然后就可以成功的禁止自动301,302了

然后,自动跳转,还需要支持meta的跳转,这个稍后再说吧,因为不管是meta跳转,还是js跳转,都涉及到一个网址补全的问题,咱们先解决了这个,再回来支持meta跳转和js跳转

9、自动完成网址补全,不需要我们在根据采集目标,提取到链接后自己再计算

在日常采集过程中,我们经常会碰到页面内链接地址缺少域名,有的则是有域名但没有协议。。。还有其他各种不应该出现的协议。。。。HMMMMMMM,反正经历的多了,自然就知道

这次,我们用政采网ccgp.gov.cn的首页来实验一下

from spider import Ajax
ajax = Ajax()
#html = ajax.Http('http://news.baidu.com/ns?word=school&ie=gb2312&cl=2&rn=20&ct=0&tn=newsrss&class=0')
html = ajax.Http('http://www.ccgp.gov.cn/')
print(html)

可以看到,页面内N多的链接都是没有网址的,所以,我们需要在采集的时候就处理掉,得到的完整的链接地址,方便我们后续处理。这个时候,该正则大显身手了。对了,在做这个补全之前,我们看看scrapy有没有补全,好像用scrapy的人很多。

行吧,咱也来次最简单的 scrapy 采集,不管那么多,只采集各首页看看

先在命令行运行几个指令

d:\>pip install scrapyd:\>scrapy startproject ccgpd:\>cd ccgpd:\ccgp>scrapy genspider ccgp_gather www.ccgp.gov.cn

的确是很简单就建立了一个针对 ccgp 的采集

然后修改其中一些文件

找到 settings.py,修改 robotstxt_obey,不验证robots.txt

找到 middlewares.py,修改 process_request 方法,在这里追加上 user-agent 信息

找到 ccgp_gather.py,修改 parse 方法,保存我们采集到的首页内容

然后回到命令行,运行采集

d:\ccgp>scrapy crawl ccgp_gather

很好,这个页面被采集下来了,我们来瞅瞅看

得嘞,他也没有进行url补全,顺带一说,感觉用 scrapy 做采集相对来说更麻烦了一点,我以前已经建立了n多的xml,针对自己的采集规则有完整的内容了,什么翻页采集,什么时间范围内采集,什么标题过滤,我们做采集,很少有整站采集,也很少有无脑采集,所以这个scrapy如果要实现以上这些需求,感觉还是挺麻烦的,每个站点都这么搞一次。。。我不如把所有站点信息放到一个xml里,用统一的规则,使用自己的爬虫解析器来一次采集多个站点。总而言之,scrapy和老顾是有缘无分了。但是,如果使用scrapy也并不影响阅读本文哦,大家可不要放弃继续阅读哦。

回到我们自己的url补全上来,再在 Ajax 类里追加一个私有方法 __url_complemented,在 http方法,return html 前,用这个方法修正一下再返回

嗯。。。。。分析下,哪些地方是url?同学们自己列举下哦

有 href ,很常见的,a标签,link 标签

有 src ,也很常见的,script标签,img标签,embed等

还有一些容易被忽略的,url,存在于style中,样式文件中,meta中。。。。

以及更不容易发现的,location,open,存在于 js 之中。。。。action,存在于 form 标签

好了好了,越说越复杂了。。。我这里仅仅先实现前两个啊,再加一个meta,其他的我不考虑哦

做url补全其实也很简单,用正则把 url 提取出来,验证 url 是否是合法的 url,当然特例要排除,什么 about:blank 啦,什么 file:/// 这种本地文件啦,什么 base64 数据(图片src可能会有这个情况)啦。。。总之,只对需要进行补全的 url 进行计算,人本身已经是带协议的就不再操作了

哪些需要补全呢?

1、没有带协议的,比如 //blog.csdn.net,鬼知道他是 http还是https。。。其实这个是由当前页面协议决定的,你在 http域名页面点这个链接,这个结果就是 http://blog.csdn.net,你在https域名页面点开这个链接,结果就是 https://blog.csdn.net

2、链接地址路径不全的,比如 /superwfei,需要补全上域名,得到 blog.csdn.net/superwfei。这种情况相对来说比较复杂,有可能会碰到需要路径计算的时候,比如 ../../../image/xxx.shtml,也有可能碰到不太规范的路径 ..../image/xxx.shtml

老顾碰到的基本就这两种情况,如果有其它情况的,可以告知老顾,再继续研究研究

下面是实现代码

 def __url_complemented(self,html):html = re.sub('''(url|src|href)\\s*=\\s*(['"]?)([^'"]*)(\\2|[\\s\\r\\n\\t])''',self.__url_replace,html,count=0,flags=re.I)return htmldef __url_replace(self,m):url = m.group(3).strip()# about:setting、about:blank 类型的,javascript:void(0) 类型的,#类型的,原样返回if re.search('^(#.*|javascript:.*|[a-z_-]+:[a-z_-]+)$',url,re.I):return m.string[m.span()[0]:m.span()[1]]# 带有协议的,原样返回,例如 https://、ftp://、file://、tencent://等if re.search('^[a-z]+://',url,re.I):return m.string[m.span()[0]:m.span()[1]]# base64 格式,原样返回if re.search('^base64',url,re.I):return m.string[m.span()[0]:m.span()[1]]root = re.sub(r'^([a-z]+:/{2,}[^/]+).*','\\1/',self.current_url.strip(),re.I)if re.search('^/(?!/)',url,re.I):url = re.sub('^/',root,url,re.I)elif re.search('^//',url):url = re.sub('^([a-z]+:)//.*$','\\1',root,re.I) + urlelse:path = re.sub('/[^/]*$','',self.current_url) + '/'p = re.search(r'^[\./]+',url,re.I)if p:# 具有 ./ 计算路径# 获取开头的全部计算路径p = p.string[p.span()[0]:p.span()[1]]# 去掉路径中 ./ 后,剩余的点的数量,就是路径向上路径的层级p = re.sub(r'\./','',p)# 获得剩余点的数量,得到层级p = len(p)pth = pathfor i in range(p):pth = re.sub('[^/]+/','',pth,re.I)if len(pth)<len(root):pth = rooturl = pth + re.sub(r'^[\./]+','',url)else:# 无 ./ 计算路径,当前路径url = path + urlreturn m.group(1) + '=' + m.group(2) + url + m.group(4)

在 Http 方法,return 前追加一行

     self.html = self.__url_complemented(self.html)

然后,我们再次对 ccgp 首页进行一下采集,看看链接都有什么

import refrom spider import Ajax
ajax = Ajax()
#html = ajax.Http('http://news.baidu.com/ns?word=school&ie=gb2312&cl=2&rn=20&ct=0&tn=newsrss&class=0')
html = ajax.Http('http://www.ccgp.gov.cn/')
urls = re.findall('''(url|src|href)\\s*=\\s*(['"]?)([^'"]*)(\\2|[\\s\\r\\n\\t])''',html,re.I)
print(len(urls))
for i in urls:print(i[0],'=',i[1],i[2],i[3])

嗯,本次采集,一共344个网址,除了 # 和 javascript:,其他的网址都有 http或https协议,是完整的url了

包括图片、下载地址、页面地址,这就方便很多了,需要注意的是,因为无法自动判定给出的采集地址到底是目录,还是文件,所以需要人为协助一下,路径的,传入的参数后边追加一个/哦,这样我们就方便计算路径了

------------------------------------------------------

那么,今天我们就先这样,本文完成了采集类的7、8、9三个需求,暂时日常采集就可以满足了,下一篇文章,我们做个实战,然后有机会了,再继续完善后边的需求。js自动跳转和meta自动跳转,也一起放到下次完善的时候再说吧。

文盲的Python入门日记:第二十八天,封装一个自定义爬虫类,用来执行日常的采集(二)相关推荐

  1. 文盲的Python入门日记:第六天,继续完善我们的MsSql类,支持查询结果的筛选和排序,以及日期类型

    文盲的Python入门日记:第一天,Python环境搭建 文盲的Python入门日记:第二天,连接到mssql和python代码书写初体验 文盲的Python入门日记:第三天,用一些小练习感受一下py ...

  2. python入门(5)使用文件编辑器编写代码并保存执行

    python入门(5)使用文件编辑器编写代码并保存执行 两款文本编辑器: 一个是Sublime Text,免费使用,但是不付费会弹出提示框: 一个是Notepad++,免费使用,有中文界面: 请注意, ...

  3. 鸿蒙harmonyOS封装一个Toast工具类

    鸿蒙harmonyOS封装一个Toast工具类 文章目录 鸿蒙harmonyOS封装一个Toast工具类 前言 一.参数都弄上即可 二.使用步骤 1.封装 2.使用 总结 前言 无语 提示:以下是本篇 ...

  4. Python入门(第二节):Python编译器安装

    一 前言 上一篇Python入门(第一节):Python版本如何选和安装末尾我放了一个投票 大家选择的是vscode,我们就来一篇vscode安装使用讲解 其实vscode和pycharm一个是插件下 ...

  5. Android开发-差点儿被Logcat骗了,用Logcat输出一行有长度限制,用递归方式封装一个Logcat工具类

    问题描述: 最近,调试一个程序,差点儿被一个简单的Logcat日志蒙蔽了双眼. 过程是这样的:在代码中,用Log.i(TAG, msg)来输出msg的信息.输出的结果却只输出了前面一部分,后面的字符没 ...

  6. Python入门学习---第二天

    循环,字典和set 循环 for和while循环 废话不多说,看代码: # for...in循环,依次把list或tuple中的每个元素迭代出来,看例子: names = ['Michael','Bo ...

  7. Python学习日记-第二十三天-飞机大战(游戏背景设计思路)

    系列文章目录 目标 ·背景简体滚动的思路确定 ·显示游戏背景 前言 这一章节是讲述的飞机大战游戏背景的设计思路细节. 一.背景简体滚动的思路确定 ·游戏启动后,背景图像 会连续不断的向下移动 ·视觉上 ...

  8. Python学习日记-第二十四天-飞机大战(敌机出场设计)

    系列文章目录 ·使用定时器添加敌机 ·设计Enemy 类 前言 昨天因为有事,所以没有及时更新 一.使用定时器添加敌机 游戏启动后,每隔一秒会出现一架敌机 每架敌机向屏幕下方飞行,飞行速度各不相同 每 ...

  9. Python 学习日记第二篇 -- 列表,元组

    一.列表 列表是一个可以包含所以数据类型的对象的位置有序集合,它是可以改变的.    1.列表的序列操作(Python3) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...

  10. Python学习日记-第二十六天-飞机大战(发射子弹和碰撞检测)

    系列文章目录 1.发射子弹 2.碰撞检测 一.发射子弹 1.1   添加发射子弹事件 Pygame的定时器使用套路非常固定: 定义定时器常量--eventtid 在初始化方法中,调用set_timer ...

最新文章

  1. 抓取apache2的进程pid
  2. 文本文件 java,java 写文本文件
  3. [html] DOM和BOM有什么区别?
  4. Maven配置项目依赖使用本地仓库的方法汇总
  5. 如何对Windows Server 2008上的ARP缓存进行管理
  6. springboot的配置文件加载的顺序,以及在不同位置配置下,加载的顺序
  7. 合同管理系统主要增加了安全性,对于大型企业作用重大
  8. ele表单验证的数字的坑
  9. 红旗Linux系统安装教程
  10. gcc用-O0优化无问题,-O3优化时程序崩溃的问题
  11. H5通过数据流方式播放视频
  12. xshell连接不上虚拟机Linux
  13. em在聊天中是什么意思_emmmm是什么意思 聊天中emmmm是什么意思什么梗
  14. 为什么我们求职 找工作 一定要写好简历?
  15. java包含不区分大小写_Java不区分大小写。
  16. 框架楼是什么意思_什么是框架结构的房子?
  17. Java实现用汉明距离进行图片相似度检测的
  18. 麦克风阵列声源定位 SRP-PHAT
  19. 光纤收发器的原理及应用_光纤收发器工作原理 - 光纤收发器工作原理及技术详解(分类,使用方法及接线图)...
  20. 计算机中的英语六级作文万能模板,英语六级作文万能模板(4篇)

热门文章

  1. html5 合成两张图片,HTML5 Canvas笔记——图像合成
  2. 使用 Sun Jimi 进行图像格式转换
  3. 跳槽理由—你的跳槽理由合理吗
  4. android原生支持机型,首发机型Find 7:OPPO发布原生Android系统
  5. ITTAGE分支预测器 “A 64-Kbytes ITTAGE indirect branch predictor”
  6. 关闭Tomcat报错The web application [ROOT] appears to have started a thread named [Abandoned connectio
  7. STM32 BOR/POR/PDR介绍
  8. 计算机常用的启动方法有,加快电脑开机、运行的常用手段
  9. 计算机打字皮肤怎么退出,使用搜狗输入法电脑版中设置快捷键更换皮肤的方法...
  10. php抢票程序,python并发编程多进程 模拟抢票实现过程