查看原文
其他

实战演练-如何获取众筹项目的团队信息

数据分析中心 Stata and Python数据分析 2022-03-15

本文作者:周宏杰

文字编辑:余术玲

技术总编:张   邯


网络公开数据爬取在日常数据获取的应用中越来越广泛,对于单页面信息单次爬取就可以完成,但通常情况下单次爬取是无法得到我们所需信息的,往往需要进行二次爬取。本文将以获取“迷你投”网站中各投资项目创始团队的成员信息为例,来介绍如何使用Python来进行二次爬取。


一、网站介绍

“迷你投”的网址为https://www.minipo.com/list/invest,它的主页面如下:

我们可以通过点击项目名称进入其对应的页面,例如点击军工缔造者,我们可以进入相应的网页中,这里也有本例所要爬取的信息——创始团队信息,如图所示就是我们需要的信息:

想要完成上述的爬虫过程,一般需要从主页面中获取关键信息,这个信息就像一把钥匙,能帮我们成功进入第二级页面,获取目标信息。点开三个第二级页面(进入第二级页面需先在此网站上注册登录,并且爬取数据时也需保持登录状态):

通过观察发现,这三个页面的网址只有一个地方不同,即网址的数字部分有所区别。这些数字就是我们需要从主页面获取的关键信息——id
查找信息操作流程:在谷歌浏览器中,按键盘上的F12,进入开发者模式——再按F5刷新页面,找到我们需要的list文件——点击Preview,点开页面的小三角形,展开被堆积的信息。在这里我们发现所需的id信息,现在我们来把它爬下来。点击Header,得到真实网址URL为“https://www.minipo.com/api/project/stock/list”,访问方式为POST,即爬虫时需要采取的方法是POST请求

至此,我们已经大致了解了该网站的情况,爬取信息的思路正如上述所述,接下来介绍如何利用Python把该过程实现。
 
二、导入Python标准库
本案例中我们将用到三个标准库,os用来创建文件夹并更改缺省路径;requests用来抓取网页源代码;json用来提取源代码中我们需要的信息:
import osimport jsonimport requests


三、获取主页面id信息
因为是POST请求,所以我们需要构建好正确的headers和data。headers和data的具体信息可以在网页开发者模式中的list文件的Headers部分找到,headers由Request Headers中的accept开始至结尾的内容赋予,data由整个Form Data内容赋予。具体信息如下:

此时,我们就可以开始爬取id信息了,首先爬取第一页的id信息。具体命令行如下所示:
import osimport requestsimport json url='https://www.minipo.com/api/project/stock/list'headers= { 'accept': 'application/json, text/plain,*/*', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'authorization':'446ebd279c16a66f1ed49d6a2e1b60ef6a1b6a64e0994c61ded3ebf7e3ff7f94', 'content-length': '32', 'content-type':'application/x-www-form-urlencoded;charset=UTF-8', 'cookie': '_ga=GA1.2.1746701206.1562657611;_gid=GA1.2.1392197811.1562657611;Hm_lvt_3cc5eb43ee2bec55f862cdef86106e7d=1562657611;minipossid=c54a8qc3dc1gv65s7hpjdhb789; minipo_token=446ebd279c16a66f1ed49d6a2e1b60ef6a1b6a64e0994c61ded3ebf7e3ff7f94;Hm_lpvt_3cc5eb43ee2bec55f862cdef86106e7d=1562658540;_gat_gtag_UA_128202395_1=1', 'origin': 'https://www.minipo.com', 'referer':'https://www.minipo.com/list/invest', 'user-agent': 'Mozilla/5.0 (Windows NT 6.3;Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100Safari/537.36', } formdata={ 'page': '1', 'size': '16', 'status': '', 'keywords': '', }id_single= requests.post(url,headers = headers,data = formdata)dicta= json.loads(id_single.text) #将已编码的JSON 字符串解码为 Python 对象print(dicta)
下图为输出的部分结果:

从上图我们可以看到输出结果是一个字典,而‘data’对应的值中又是一个字典,而第一层字典中‘data’对应的字典才含有我们想要的id。因此我们可以通过‘data’提取第一层字典的值,得到一个新字典,再通过‘data’,提取第二层字典的值,该值是一个列表,各个项目的项目信息作为元素。通过指定索引值就能获得对应项目的信息,得到一个字典,最后通过‘id’得到id信息。具体命令行如下:
idget=[]for i in range(len(dicta['data']['data'])): #因为不同页面可能项目数不同,因此用len()命令来获取每一页网页的项目数 dicta = json.loads(id_single.text)['data']['data'][i]['id'] idget.append(dicta) #将一个主页面的每个项目的id信息拼在一起print(idget)

输出结果如下:

本项目获取信息的主页面有6页,因此需要编写一个循环来获取所有主页面的id信息。具体命令行如下:
import osimport requestsimport json
idget = []for num in range(1,6): url='https://www.minipo.com/api/project/stock/list' headers = { 'accept': 'application/json, text/plain, */*', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'authorization': '446ebd279c16a66f1ed49d6a2e1b60ef6a1b6a64e0994c61ded3ebf7e3ff7f94', 'content-length': '32', 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8', 'cookie': '_ga=GA1.2.1746701206.1562657611; _gid=GA1.2.1392197811.1562657611; Hm_lvt_3cc5eb43ee2bec55f862cdef86106e7d=1562657611; minipossid=c54a8qc3dc1gv65s7hpjdhb789; minipo_token=446ebd279c16a66f1ed49d6a2e1b60ef6a1b6a64e0994c61ded3ebf7e3ff7f94; Hm_lpvt_3cc5eb43ee2bec55f862cdef86106e7d=1562658540; _gat_gtag_UA_128202395_1=1', 'origin': 'https://www.minipo.com', 'referer': 'https://www.minipo.com/list/invest', 'user-agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36', }
formdata={ 'page': f'{num}', #f代表的是format,指明这里的括号是存在的,而不是字符串 'size': '16', 'status': '', 'keywords': '',
} id_single = requests.post(url,headers = headers,data = formdata)
a = json.loads(id_single.text) for i in range(len(a['data']['data'])): dicta = json.loads(id_single.text)['data']['data'][i]['id'] #将已编码的 JSON 字符串解码为 Python 对象 idget.append(dicta)print(idget)
输出结果如下:

总共储存了67个id。

四、创始团队成员信息的爬取

以爬取“军工缔造者”项目的创始团队信息为例,该页面内容如下:

该页面访问方式也是POST请求,因此需要找到url

headers、data。

url、headers、data具体内容正如上图所示,分别为RequestURL、Request Headers、Form Data。接下来即可爬取所有项目创始团队信息,具体命令行如下:

teaminfo=[]for id1 in idget: url1=f'https://www.minipo.com/api/project/stock/{id1}/detail' headers1 = { 'accept': 'application/json, text/plain, */*', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'authorization': '446ebd279c16a66f1ed49d6a2e1b60ef6a1b6a64e0994c61ded3ebf7e3ff7f94', 'content-length': '32', 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8', 'cookie': '_ga=GA1.2.1746701206.1562657611; _gid=GA1.2.1392197811.1562657611; Hm_lvt_3cc5eb43ee2bec55f862cdef86106e7d=1562657611; minipossid=c54a8qc3dc1gv65s7hpjdhb789; minipo_token=446ebd279c16a66f1ed49d6a2e1b60ef6a1b6a64e0994c61ded3ebf7e3ff7f94; Hm_lpvt_3cc5eb43ee2bec55f862cdef86106e7d=1562658540; _gat_gtag_UA_128202395_1=1', 'origin': 'https://www.minipo.com', 'referer': 'https://www.minipo.com/list/invest', 'user-agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36', }
formdata1={ 'id': f'{id1}' } team = requests.post(url=url1,headers = headers1,data = formdata1) team_dicta = json.loads(team.text) teaminfo.append(team_dicta) #这里把所有含团队信息的页面都连在一起print(teaminfo)

部分输出结果如下图所示:


五、创建csv文件并保留需要内容

从输出结果可知,我们得到了大量的团队信息,但是大部分信息是没有用。接下来,我们新建一个csv文件并保留我们需要的信息。我们需要的信息是项目名(name_program)、id、项目创立时间(start_time)、成员姓名(name_person)、职位(position)、简历概述(desc)、项目计划筹资(cf_raising)、项目最大筹资(cf_max_raising)、项目目前成功筹资(cf_success_raising_offer)、目前筹资比例(current_rate)。
具体命令行如下:
varname = ['programname', 'id', 'start_time','cf_raising', 'cf_max_raising', 'cf_success_raising_offer','current_rate','personname', 'position', 'desc']varname1 = ['name', 'id', 'start_time', 'cf_raising','cf_max_raising', 'cf_success_raising_offer', 'current_rate']varname2 = ['name', 'position', 'desc']with open("D:\\minipo.csv", "w",encoding="gb18030") as f: # 以读写方式创建"minipo.csv"文件并返回文件对象"f" f.write("\t".join(varname) + "\n") # f.write()是输入括号内的内容。将varname中的元素用制表符\t隔开输入csv文件,并让每个元素作为每一列的标题 for unit in teaminfo: #unit是每个项目 info = [] for i in varname1: info.append(str(unit['data'][i]).replace("\n","").replace("\t", "").replace("\r","")) #为避免原数据中自带的换行符、制表符、回车符导致格式混乱,需要去除自带的这些符号 if type(unit['data']['team']) is list: # 当没有团队信息时,会返回字符串None,为了避免的None的影响,我们需要只对列表也就是有信息的部分进行处理 for u in unit['data']['team']: #u是每个成员 info1 = [] for j in varname2: info1.append(str(u[j]).replace("\n","").replace("\t", "").replace("\r",""))               f.write('\t'.join(info)+'\t'+'\t'.join(info1)+"\n")

以上就是爬取“迷你投”各项目创始团队成员信息的整个过程。

对我们的推文累计打赏超过1000元,我们即可给您开具发票,发票类别为“咨询费”。用心做事,不负您的支持!
往期推文推荐
       Zipfile(一)       
        tabplot命令
Jupyter Notebook不为人知的秘密
字符串方法(三)
数据,我要“拷打”你
花式调用返回值 —— svret与storedresults
encode 和decode——带你探索编码与解码的世界
字符串方法(二)
如何快速生成分组变量?
用Stata实现数据标准化
字符串方法介绍
Jupyter Notebook的使用
Stata16新功能之“框架”——frlink连接多个数据集(3)
Stata16新功能之“框架”——基础命令大合集(2)
三分钟教你读懂Python报错
解析XML文件
命令更新之reg2docx:将回归结果输出到word
命令更新之t2docx——报告分组均值t检验

关于我们

微信公众号“Stata and Python数据分析”分享实用的Stata、Python等软件的数据处理知识,欢迎转载、打赏。我们是由李春涛教授领导下的研究生及本科生组成的大数据处理和分析团队。

此外,欢迎大家踊跃投稿,介绍一些关于Stata和Python的数据处理和分析技巧。
投稿邮箱:statatraining@163.com
投稿要求:
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿+推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到有关数据处理、分析等问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存