爬虫的小tricks

Posted by grt1stnull on 2017-10-13

0x00.前言

最近写了一个小爬虫,结果遇到了一些问题,经过一番风雨解决了。并且开发出了新姿势,这里介绍一下。

0x01.优雅的会话机制

emmm是这样的,提交表单到登录页面,结果只返回没有登录。因为登录后会重定向到主页,所以我怀疑过程中重定向是不是发生了什么。

1
2
3
imoprt requests
r = requests.post("https://github.com/session", headers=headers, data=payload, allow_redirects=False, cookies=_cookies)

并且还加了个循环,获取http头部中的Location字段,循环跟着跳转,并且带上了cookies,结果并没有什么卵用。

虽然之后我解决了这个问题,但是要从下面说起。

1.http协议中的cookies

众所周知,http是无状态协议,而cookies是会话的标志,用于区分用户等等。在服务端通常会保存为session,存储到文件里或是消息队列,比如redis里。

那么cookies在http协议里是怎样传递的呢?首先服务端向客户端发送标识设置cookies,通过http头部的set-cookie,如图:

spider-tricks

客户端又是怎样传递cookies给服务端的呢?也是基于http的头部,通过cookies字段。如图:

spider-tricks

经过分析发现,原来我的爬虫在POST请求时竟然没有在头部中附带cookies,于是就被重定向了。经过测试,发现POST竟然都不能带cookies,简直是玄学啊。结果第二天它自己好了…

2.requests.session()对象

话说当时发现了问题,但是对于这种玄学完全没有能力应当啊。

百度一下,希望能够有人和我有一样的问题吧,于是百度requests post cookie,发现了Python requests如何将第一个请求得到的 cookie 通过 POST 提交给第二个请求?

虽然回答不多,但里面讲的绝对是瑰宝级别的。

requests 有一种用法是,通过一个 requests.Session 对象,来完成请求在上下文传递过程中的 Cookie 处理。

于是写代码:

1
2
3
4
5
6
7
import requests
s = requests.Session()
# 获取cookie
req = s.get('http://xxxxx', headers=headers)
# 传递cookie
req = s.post('http://xxxxx', headers=headers, data=data)

顺利登录。

所以如果有需要,可以用这种办法传递cookie,比手动更有效、方便。

3.cookies储存与读取

虽然上一步我们顺利登录了,但是每次都要登录是不是太麻烦了。甚至可能引起这个网站的警觉。

于是我又谷歌了requests cookie save file,又找到了How to save requests (python) cookies to a file?

里面已经给出了成型的代码,美滋滋。

试了一下里面的代码,一个问题是cookies本地序列话后貌似是二进制字符串,所以打读取和写文件都要选择二进制方式,即b(binary)。

代码如下:

1
2
3
4
5
6
7
8
9
import pickle
# 把cookies写入文件
with open('./cookies.txt', 'wb') as f:
pickle.dump(requests.utils.dict_from_cookiejar(session.cookies), f)
# 从文件中读cookies
with open('./cookies.txt', 'rb') as f:
cookies = requests.utils.cookiejar_from_dict(pickle.load(f))

0x02.优雅的爬虫调试

我总结了三种(我)常用的爬虫调试方法:

  1. 直接输出
  2. 保存html
  3. 使用代理

1.直接输出

比如访问某个网站,结果发现lxml什么都没有解析到,或者结果与预期不符合。这时候,很方便,直接一句话print(req.content)把源码输出来,看一下到底哪里出了问题。

但是这种方法只能处理一些简单的问题,好处是简单直接。

2.保存html

这实际上是上一种方法的扩展,有时候源码一输出一大片,js代码也有,写的很乱(没错,新浪微博,说的就是你)。这个时候输出了也不方便分析,于是保存到html,本地打开看一下。

很简单:

1
2
3
4
def see(text):
with open("./t.html", "w") as f:
f.write(text)
see(req.content)

但是呢,也只到此为止了。更高级一点可能尝试输出http头部。

3.使用代理

这是我这次调试中突然想到的办法,作为代理,可以截获所有通过的流量。本来想使用fiddle,但是在linux下支持不好,于是使用burp suite。

首先设置proxies,requests官网的介绍如下:

spider-tricks

但是这里我们要注意https这个协议,https使用证书来验证网站的可靠性,并且https协议的内容是加密传输的(http则是以明文传输的)。

服务器布置好证书后,客户端访问网站,先看证书有没有过期、是不是颁发给这个域名的等等,但是最重要的,这个证书的颁发机构在不在可信机构里,是不是服务器伪造的。

所以在通过代理对https进行抓包时,都会把代理软件的证书导入到可信机构中。一般的,在电脑上,我们会导入浏览器。但是这里,对这个框架我们怎么导入呢?这里直接设置verify=false,即不对这个证书进行验证。

spider-tricks

之后设置好我们burp suite的监听端口:

spider-tricks

再次运行要调试的爬虫程序,可以看到:

spider-tricks

我们也可以在历史中继续对http请求进行分析:

spider-tricks

0x04.总结

没啥好总结的,end.