需求
给出关键字,然后爬取财政部ppp数据库中相应项目的数据,保存到sqlite中。
使用软件:requests、json、sqlite
代码写于2020-4-22,以后可能会出现网页改版而导致不可用
先上代码:
import requests
import json
import sqlite3
db = sqlite3.connect('./caizhengbu.db')
cursor = db.cursor()
cursor.execute('create table if not exists ppp (项目名 TEXT)')
db.commit()
def ppp():
headers = {# POST /api/pub/project/search-store HTTP/1.1
'Host': 'www.cpppc.org:8082',
'Connection': 'keep-alive',
'Content-Length': '167',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36',
'Content-Type': 'application/json',
'Accept': '*/*',
'Origin': 'https://www.cpppc.org:8082',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://www.cpppc.org:8082/inforpublic/homepage.html',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9'
}
total_num = None
cur_num = 0
pageNum = 1
while True:
# payloadData = {"name":"医疗","industry":"",
# "min":0,"max":10000000000000000,
# "pageNumber":pageNum,"size":5,"level":"","start":"","end":"","dist_province":"","dist_city":"","dist_code":""}
# 选择领域中,医疗卫生是016,准备阶段的代码是status,1是准备,2是采购,3是执行
payloadData = {"name":"","industry":["016"],"min":0,"max":10000000000000000,
"pageNumber":1,"size":5,"status":["3"],"level":"",
"start":"","end":"","dist_province":"",
"dist_city":"","dist_code":"","created_date_order":"desc"}
# 储备清单和管理器项目区别在search还是search-store
r = requests.post('https://www.cpppc.org:8082/api/pub/project/search',
data = json.dumps(payloadData),
headers = headers)
# print(r.text)
js = json.loads(r.text)
# js['data']中有total,代表检索出来结果数量
total_num = int(js['data']['total'])
for dt in js['data']['hits']:
proj_id = dt['proj_rid']
get_single_project(db, cursor, proj_id=proj_id)
cur_num += 1
pageNum += 1
if cur_num >= total_num:
break
def get_single_project(db: sqlite3.Connection, cursor: sqlite3.Cursor, proj_id = '3a8d834ef9fe41bba342f48d9ca00d33'):
headers = {
'Host': 'www.cpppc.org:8082',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36',
'Content-Type': 'application/json',
'Accept': '*/*',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://www.cpppc.org:8082/inforpublic/homepage.html',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
}
# url = 'https://www.cpppc.org:8082/api/pub/project/prepare-detail/f7e66ad9651f4193942952fb3fc928d2'
url = 'https://www.cpppc.org:8082/api/pub/project/prepare-detail/' + proj_id
r = requests.get(url, headers = headers)
# print(r.text)
# detail_url = 'https://www.cpppc.org:8082/api/pub/project/detail/f7e66ad9651f4193942952fb3fc928d2'
detail_url = 'https://www.cpppc.org:8082/api/pub/project/detail/' + proj_id
r = requests.get(detail_url, headers = headers)
print(r.text)
dt = json.loads(r.text)['data']
def returnMode(i):
return ["政府付费", "使用者付费", "可行性缺口补助"][int(i)-1]
def operateMode(i):
return ["BOT","TOT","ROT","BOO","TOT+BOT","TOT+BOO","OM","MC","其他"][int(i) - 1]
def projStatus(i):
return ['准备阶段', '采购阶段', '执行阶段'][int(i) -1]
proj_data = {}
proj_data['项目名'] = dt['projName'].strip()
proj_data['发起时间'] = dt['startTime'].strip()
proj_data['所在省'] = dt['distProvinceName'].strip()
proj_data['所在市'] = dt['distCityName'].strip()
proj_data['所在县'] = dt['distName'].strip()
proj_data['项目总投资'] = dt['investCount']
proj_data['回报机制'] = returnMode(dt['returnMode']).strip()
proj_data['项目概况'] = dt['projSurvey'].strip()
proj_data['运作方式'] = operateMode(dt['operateMode']).strip()
proj_data['所属行业'] = dt['industryRequiredName'] + '-' + dt['industryOptionalName']
proj_data['所处阶段'] = projStatus(dt['projStatus'])
drop = []
for k,v in proj_data.items():
if not v:
drop.append(k)
continue
cursor.execute('select * from ppp limit 1')
columns = set([x[0] for x in cursor.description])
if k not in columns:
cursor.execute(f'alter table ppp add {k} TEXT')
db.commit()
for i in drop:
proj_data.pop(i)
k = ','.join(proj_data.keys())
print(proj_data)
v = ','.join(['\'' + str(proj_data[k]) + '\'' for k in proj_data.keys()])
print(f'insert into ppp ({k}) values ({v})')
cursor.execute(f'insert into ppp ({k}) values ({v})')
db.commit()
# 运营模式在operateMode里面
# yunzuofangshiurl = 'https://www.cpppc.org:8082/api/pub/project/query/enums/1004,1005,1048'
# r = requests.get(yunzuofangshiurl, headers = headers)
# print(r.text)
ppp()
# get_single_project()
爬取过程
首先进入数据库官网:https://www.cpppc.org:8082/inforpublic/homepage.html#/projectList
如果直接使用request.get方法来爬这个页面的数据,会发现爬不下来想要的数据,基本都是js的代码。那么就需要找到页面上面的数据是从哪里来的。在chrome浏览器中,按f12开启检查模式(或者在页面上面右键,然后检查)。点击network,然后刷新页面就可以看到显示这个页面时向服务器请求了什么数据。
可以看到,一次请求服务器会返回一个html,一堆js,还有一些图片。有两个0的文件不知道是什么,但是又一个search-store看上去比较可以,打开看一看里面是什么东西。
在preview中可以看到,貌似这个页面返回的就是所有的项目了(只有头5个,怎么翻页后面再说)。那怎么拿到这个返回值的呢?看一看headers里面有什么东西。
可以看到这个请求的链接并不是官网的链接,怪不得直接request官网得不到想要的数据,而且可以看到想得到这个信息,请求使用的是post而不是get。那关键字什么的在哪里加上去呢?看看request headers
request headers里面都是比较常规的东西,没什么需要注意的,需要注意的是request payload,看到里面有一个pageNumber,貌似找到从哪里来进行翻页了。然后有个问题就是我们想要找关键字怎么找呢?再在搜索框中输入医疗,然后搜索,发现返回了一个新的search-store,里面name变成了“医疗”。然后现在在主页面需要的信息就找全了。
注意到每个项目还可以点进去,那点进去之后的数据怎么爬呢?随便找一个项目点进去。
发现这个项目url有些编码看不懂。再多点几个项目,会发现最后一大串字符串是不一样的。那我们可以确定进入项目页面的方法就是url里面加上类似于项目id一类的东西。那项目id从哪里找呢?
我们把这个疗养院的“项目id”复制一下,在之前主页面返回的search-store的结果中搜索一下看看有没有发现
可以发现,主页面的返回结果是一个json,而proj_rid就是我们需要的项目id,拿到这个id和url拼接一下就可以找到每一个项目具体信息的链接了。
然后进入项目具体的链接,也发现直接request.get(项目url)是拿不到需要的信息的。那再用这个方法继续尝试。就可以找到返回值中每一个字段和页面中如何对应的。
在爬取项目具体信息的过程中,发现返回的json中没有bot之类的运作方式,那这个运作方式是在哪里呢?
在项目具体页面中,搜索bot,发现有4个文件包含bot,两个js不去管,看到一个叫做1004什么什么的文件比较可以,进去继续搜一下,发现果然找到了我们需要的内容,原来运作方式这个字段使用代码隐藏起来了。这个代码对应的是哪个字段呢?
如果直接搜dictcode,会发现有两个文件有这个东西,第二个js文件打开之后会发现并不是我们想要的东西。
一个一个翻阅文件,发现返回的这么多东西里面,有两个是我们感兴趣的。在第二个文件里面,有一个operatemode项
多检查几个就会发现这个operatemode就是我们想要的内容