不知不觉间爬虫的任务越来越多了,我都开始怀疑是不是当时写简历的时候手一抖,面成了爬虫工程师。
1. 简介
也没有用到什么高深的东西,ip有限,带宽有限,大多都是挂个代理单进程单线程,偶尔用到多线程。
主要也就是记录一下成长历程,思路什么的简单来说就是让你的程序看起来像个人
2. 网页端
很多网站可能都设置了各种各样的陷阱来判断你是人还是机器,我们这边其实从需求端就砍掉了一些难搞的网站,比如某程……常爬的比如某点评,某房,还有某电影,某居客也会偶尔爬一下。
也不能说爬虫这东西对人家都是害处,毕竟还贡献了不少访问量是不(大雾)但一定要注意点爬取的力度,以及该伪造的信息要伪造好。
2.1 准备
- 首先呢,ip代理池这东西最好要有。个人的话无所谓,被发现了封了就封了,但是对公司来说搞不好会被同事打的。
- headers伪造。该改的记得要改,User-Agent这个不用多说,这个都不改的话还是别爬了……Cookie,Host也挺关键的,某房针对不同页面的Cookie,Host都是动态的,找到生成的关系还是能破解的。Referer对某些网站也挺关键的,比如某点评会检测,可以统一设置成一个让人家没法说你是假的的网址。
- 速度控制。这个很关键,你隐藏的再好一秒钟几十个网址也太假了。而且本来就做着爬虫了还超额的给人家服务器增大负担,良心不会痛吗。
2.2 具体问题
程序真正跑起来都是不断调试的。说几个踩过的坑:
- 写好重试机制,比如一个url出错了就重试三遍。有些时候请求没有返回预期结果可能是各种原因并不单单是因为你的代码有问题。
- 写好续传机制,同上,别偶然出个错程序就崩了,然后你还不知道跑到了哪,从头开始?什么redis啊,mongo啊该用的用起来。
- 出了问题先想办法把问题简单化。比如说你的代理ip被人家怀疑了,要你输入验证码,这个时候不是先想办法破解掉验证码,这个需要的时间可能太久了,完全可以chrome挂上代理,浏览器访问一下点掉就好了。
还有的一些网站加的验证比较蠢,比如说让你访问另一个url得到验证码,这种可以考虑下破解掉。 - 代码规范化,公用的部分封装好,注释写好,最好更新日期都带上
这样一方面是有利于复用,另一方面也是避免代码混乱。面对那些经常更新html的网站把每一个版本的html解析都保存好,没准过几天它们就改回原版了,真的,别问我为什么知道。 - 代码结构设计
比如说先爬搜索的列表页,获取每个页面的首页链接,再从首页获取详情页链接,xx页链接,再分别进入这两个页面抓必要信息。
这时候上个mongodb会让整个过程清晰很多。
简单放下代码:
from fake_useragent import UserAgent
import requests
import time
s = requests.Session()
TIMEOUT = 10
def get_user_agent():
ua = UserAgent()
return ua.random
def get_sofang_headers(version, url):
if version == "列表页":
headers = {}
elif version == "首页":
headers = {}
elif version == "详情页":
headers = { ... }
return headers
def fetch(url, retry=0, version="列表页"):
if url:
url = url.strip()
if "http" not in url:
return ""
# 代理模式1
choice_proxy_item = choice_dp_proxy()
proxies = {
'http': choice_proxy_item,
'https': choice_proxy_item
}
# 代理模式2
# from ..commons.common_params import proxy as proxies
s.headers = get_sofang_headers(version=version, url=url)
s.headers.update({'user-agent': get_user_agent()})
try:
res = s.get(url, timeout=TIMEOUT, proxies=proxies)
if res.status_code != 200:
print(res.status_code)
raise Exception
res.encoding = res.apparent_encoding
return res
except (requests.exceptions.RequestException,
requests.exceptions.ProxyError) as e:
if retry <= 3:
print("休息会")
time.sleep(2)
return fetch(url, retry=retry + 1, version=version)
except Exception as e:
if e:
print(e)
if retry <= 3:
print("着重休息会")
time.sleep(2)
return fetch(url, retry=retry + 1, version=version)
代理模式1
import random
DP_PROXIES = [
'ip:port',
...
]
def choice_dp_proxy():
if DP_PROXIES:
return random.choice(DP_PROXIES)
return ''"
代理模式2 -- 阿布云
# 代理服务器
proxyHost = "http-dyn.abuyun.com"
proxyPort = "9020"
# 代理隧道验证信息
proxyUser = ".."
proxyPass = ".."
proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % {
"host": proxyHost,
"port": proxyPort,
"user": proxyUser,
"pass": proxyPass,
}
proxy = {
"http": proxyMeta,
"https": proxyMeta,
}
常用方法:
r = requests.get(url)
r.text HTTP相应内容的字符串形式
r.content HTTP相应内容的二进制形式
r.encoding 从HTTP的header中猜测编码方式
r.apprentencoding 备选编码,从内容中分析出的编码方式
r.status_code
r.request.url
异常处理
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = r.apprentencoding
except:
......
查看响应时间
r.elapsed.microseconds
3. app端
有时候网页端满足不了全部需求了,于是毒手又伸向了app。
比如说某德地图,要抓取它购物中心内的店铺信息。
嗯,这个时候需要借助一些软件了,我是选的mitm,感觉上手比较简单。
简单说下流程,就是
- 电脑上装好,启动,手机和电脑连同一wifi,指定ip、端口,手机上安装证书。
- 写好url解析脚本,电脑上重新启动服务,手机开始浏览需要的页面。
- 数据处理。
常用命令:
启动: mitmproxy
启动python脚本过滤抓包:mitmdump -s xxx.py
xxx.py
class AmapCapture():
def response(self, flow):
"""
1. 文件以 req_date_time.json
"""
t = time.time()*1000
if "ws/valueadded/shopping/poi_list" in flow.request.url:
f = open("res/res_{t}_{name}.json".format(t=t, name="上海森宏购物中心"), "wb")
f.write(flow.response.content)
f.close()
def start():
return AmapCapture()