背景
之前我从来没有爬过网页,一直对scrapy很好奇,这周一粽子让我去爬一下
http://www.calltrace.cn/home.html的
工单管理-》已提交结果工单-》举报内容摘要/来电时间,一共有100多条,看起来需要一条一条点开才行。
我之前对网页有了一点小小的积累,大致明白了cookie,post,get,html这方面的知识,因此觉得应该是能够胜任的,因此就愉快得答应了下来。
不过我一开始走了不少弯路,最后抓出网页信息的时候,也是瞎猫碰到死耗子,并不能说以后就能成功了,而且或许这个换台电脑,关闭一下浏览器未必能够成功。
我的学习路线大致是这样的:
1、我先查看了网页源码和调试模式下的element元素审查
然后我就发现源码里面并没有完整的信息嘛,只是一个html的骨架
而element里面的信息到时全面的,因此我一开始想着是不是应该把网页整体拉下了,然后去解析html,把数据提取出来,而后的实践我发现至少我这次用半天时间没有做出来。
2、于是我一开始的方法是:学些久仰大名的scrapy,然后我大致了解了scrapy的原理:
几个教程:
- spider把response里面的数据提取出来,要不返回item,要不进行下一次请求
- Items是用来存储数据,就是一个model
- pipelines 负责对spider返回数据的处理
- 其它的如中间件,settings我没怎么用到,就不说了
其中最重要的是yield语法,在scrapy里面,它会产生新的线程,或者保存item,或者给出下一个请求
3、但是,在学了这些之后,我发现完全用不到这次要爬的网站,因为上面的爬虫基于url进行爬取,不过我
要爬的网站里面并没有url。。。后来我看到一个词,叫
动态网页
,可能说的就是这个
我只能去查看chrom里面的network,看看是否有get和post请求了,后来我看到了2个请求:
url_frontpage = "http://www.calltrace.cn/Entity/List.do"
url_detail = "http://www.calltrace.cn/Entity/Detail.do"
它们中一个是主页的请求,一个是详情页的请求
主页请求List.do的request中带有参数page,limit,start;response中则返回指定数量的已提交工单的编号
详情页请求Detail.do中请求参数则正好需要上面的已提交工单的编号!
因此,只要先请求List.do,把所有已提交工单的编号都拿到,然后用这个编号请求Detail.do就能获得详情页的所有信息了。
我在postman上试了一下,又填了一个坑:该网站是需要登录的,因此要用cookie访问这些接口,但postman没找到填cookie的地方,后来我又上网搜了一下,发现只要打开postman的拦截器就行了,它会自动把chrome浏览器的cookie填进来
于是,代码就很容易写了,这其中对关键字段的定位,我用了找规律的本办法,想来这样也没关系,顺带处理了异常和多线程访问
# -*- coding: utf-8 -*-
import threading
import requests
import json
cookies = {"JSESSIONID": "D4746970BB45A3A27AE9567C0E02DE32", # 该sessionid是会变化的
"rememberme": "true",
"company": "%E5%B7%A5%E4%BF%A1%E9%83%A8",
"username": "yudongsheng",
"password": "63275050"
}
url_frontpage = "http://www.calltrace.cn/Entity/List.do"
url_detail = "http://www.calltrace.cn/Entity/Detail.do"
limit = "500" # 每页展示的工单条数
count = 0 # 输出的是第几个工单
def get_url_list():
"""
调用List.do接口,获取所有已提交结果工单的id,构成一个列表返回
:return:
"""
querystring = {"_dc":"1488702544010",
"entityInfoId":"a9f19f47e32940ae920d144982f52af1",
"filterSql":"",
"roleId":"x8c8feb4f37b40be8e41626afbd8ee1c", # 固定值
"filterViewId":"x625053ea86b453c9ee0f5c629c48a5b", # 固定值
"originalViewId":"x625053ea86b453c9ee0f5c629c48a5b", # 固定值
"menuId":"x7f2acbbd6e44f888ee25340944b0d4b", # 固定值
"limit":limit,"page":"1","start":"0"} # 一页展示的工单数
headers = {
'cache-control': "no-cache",
'postman-token': "677a27f7-5545-3d25-c503-65d6c2f5d378"
}
response = requests.request("GET", url_frontpage, headers=headers, params=querystring, cookies=cookies) # 访问列表接口,获取工单ids
ret1 = json.loads(response.text)
entityRecordIds = [ele["tjjgGongdanRwgdidFk"]for ele in ret1["data"]]
return entityRecordIds
def get_jubaoneirongzhaiyao(entityRecordId):
"""
传入工单id,调用Detail.do接口,获得工单详情
:param entityRecordId:
:return:
"""
global count
querystring = {"entityInfoId": "daf39beb648143219d2ccbffc9ab94b8",
"entityRecordId": entityRecordId,
"filterViewId": "",
"roleId": "x8c8feb4f37b40be8e41626afbd8ee1c",
"specificCompanyId": "",
"specificSolutionId": "",
"menuId": ""}
payload = ""
headers = {
'cache-control': "no-cache",
'postman-token': "d1aa3193-a9df-bcbe-361b-8310c4c59df7"
}
response = requests.request("POST", url_detail, data=payload, headers=headers, params=querystring, cookies=cookies)
text = response.text
ret = json.loads(text)
# 建议下面一段代码加个锁,这样可以防止乱序
count += 1
print(u"第"+str(count)+u"条")
try:
print(ret["recordName"]+u" 举报内容摘要 "+ret['sectionList'][1]["items"][2]["items"][0]["value"]) # 获取任务编号和举报内容摘要
except:
print ret.get("recordName", u"未知错误 "+ "id: "+entityRecordId)+u" 举报内容摘要"+u" 空 "
#
def multi_thread_url():
"""
多线程获取数百个详情页的信息
:return:
"""
entityRecordIds = get_url_list()
print(u"总共有" + str(len(entityRecordIds)) + u"条")
for entityRecordId in entityRecordIds:
t =threading.Thread(target=get_jubaoneirongzhaiyao, args=(entityRecordId,))
t.start()
multi_thread_url()
最后的输出如下图所示:
4、不足
首先没有进一步了解scrapy,跳过了其中的难点,对于动态网页,应该有其他方式来抓取
其次可以做一个自动登录的动作,把cookie取到,就不用手动改cookie了
然后可以把下载的数据保存到excel里面去
最后,要能够在windows下运行