(数据科学学习手札47)基于Python的网络数据采集实战(2)
一、简介
马上大四了,最近在暑期实习,在数据挖掘的主业之外,也帮助同事做了很多网络数据采集的内容,接下来的数篇文章就将一一罗列出来,来续写几个月前开的这个网络数据采集实战的坑。
二、马蜂窝评论数据采集实战
2.1 数据要求
这次我们需要采集的数据是知名旅游网站马蜂窝下重庆区域内所有景点的用户评论数据,如下图所示:
思路是,先获取所有景点的poi ID,即每一个景点主页url地址中的唯一数字:
这一步和(数据科学学习手札33)基于Python的网络数据采集实战(1)中做法类似,即在下述界面:
翻页抓取对应每个景点poi ID的部分即可:
比较简单,这里不再赘述,最终整理成数据框,景点名和poi ID一一对应。
接着根据得到的poi ID,再对每一个景点下的评论数据分别进行采集,但和之前遇到的最简单的静态网页不同,这里的评论数据是有js控制的,即当我们在景点页面内点击评论区块的下一页按钮,界面会刷新并显示下一页的评论内容,但浏览器url栏中的url地址并无改变,这就需要用更深入的方式来获取评论区域数据的真实url地址。
2.2 目标url地址的获取
以洪崖洞页面为例,点击页面内的蜂蜂点评进入评论内容区域:
当我们点击评论数据区域下方的下一页时,评论内容翻页刷新,但浏览器地址栏中的url地址并没有发生改变:
这时我们就需要找到控制评论数据区域的真实请求地址,在浏览器中按下F12,打开开发者工具,点击network项:
选择JS,这时可以发现下面并无内容,因为这里只会记录打开开发者工具后页面内新增的内容,这时我们点击评论区域下方的后一页按钮,随着界面内容的更新,下方network中随即出现了如下内容:
这就是请求评论区域内容的真实url地址,点击它,进入如下内容:
至此,我们便找到了控制评论区域发起请求的真实地址和相关属性,接下来我们先提取一下这些内容中我们需要的部分,为正式的采集做好准备;
2.3 伪装浏览器
要伪装浏览器,我们需要将上图中的Request Headers下除了cookies的内容复制下来,整理成一个叫做headers的字典如下(其中起关键作用的是User-Agent,其他的可以不记录):
再将Request Headers下的cookies中由;分隔的内容同样整理成一个叫做cookies的字典如下:
这两个参数我们会在requests包中的get方法中传入,接下来我们来观察翻页请求url的规律;
2.4 探索url规律
我们找到下列内容中红圈指示的地方:
上面红圈中的内容是当前评论区域发起请求的真实url地址,下面红圈的内容是在当前url中的关键参数,很明显,params是一个字典,其中poi_id顾名思义即为当前景点(洪崖洞)对应的poi ID,page对应的则是当前评论内容所在的页数,just_comment这个参数我观察到在任何页中都不变,这里我们让它持续为1即可。
2.5 测试
了解到上述内容后,结合当前的url地址,可以得到下列替换规则:
http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApicallback=jQuery181042269431321668516_1534601996232¶ms=%7B%22poi_id%22%3A%22%22%2C%22page%22%3A%2C%22just_comment%22%3A1%7D&_=1534602025986
只需要控制红色区域内容的替换,我们即可实现对评论内容资源的请求,下面我们来做个测试,这里以解放碑(对应poi ID 1690)下第13页评论为例,按照上述规则,我们将网址分成素材和实时参数两部分,下面的url即为通过拼接最终得到的url地址:
url_left = 'http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApi?callback=jQuery18109093089574473625_1532513669920¶ms=%7B%22poi_id%22%3A%22'
url_middle = '%22%2C%22page%22%3A'
url_right = '%2C%22just_comment%22%3A1%7D&_=1532513718986' url = url_left+''+url_middle+''+url_right
我们在Python中进行测试,对上述url地址发起请求:
import requests #设置请求头文件
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36',
'Accept':'*/*',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Referer':'http://www.mafengwo.cn/poi/6653.html'
} #设置cookies
cookie = {
'PHPSESSID':'1nivct21bumab1adia6i7k1a82',
'mfw_uuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
'uva':'s%3A78%3A%22a%3A3%3A%7Bs%3A2%3A%22lt%22%3Bi%3A1532508009%3Bs%3A10%3A%22last_refer%22%3Bs%3A6%3A%22direct%22%3Bs%3A5%3A%22rhost%22%3Bs%3A0%3A%22%22%3B%7D%22%3B',
'__mfwurd':'a%3A3%3A%7Bs%3A6%3A%22f_time%22%3Bi%3A1532508009%3Bs%3A9%3A%22f_rdomain%22%3Bs%3A0%3A%22%22%3Bs%3A6%3A%22f_host%22%3Bs%3A3%3A%22www%22%3B%7D',
'__mfwuuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
'UM_distinctid':'164d0987902bb2-0bb3f4e59cf01f-3e3d560e-1fa400-164d098790328c',
'oad_n':'a%3A3%3A%7Bs%3A3%3A%22oid%22%3Bi%3A1029%3Bs%3A2%3A%22dm%22%3Bs%3A15%3A%22www.mafengwo.cn%22%3Bs%3A2%3A%22ft%22%3Bs%3A19%3A%222018-07-25+16%3A40%3A08%22%3B%7D',
'__mfwlv':'',
'__mfwvn':'',
'__mfwlt':'' }
url_left = 'http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApi?callback=jQuery18109093089574473625_1532513669920¶ms=%7B%22poi_id%22%3A%22'
url_middle = '%22%2C%22page%22%3A'
url_right = '%2C%22just_comment%22%3A1%7D&_=1532513718986' url = url_left+''+url_middle+''+url_right
r = requests.get(url=url, headers=headers, cookies=cookie)
得到网页内容如下:
这里的网页内容还未经过转码,这里我们使用下述方式转码,并将\替换为空字符:
'''对相应的网页内容进行转码'''
html = r.content.decode('unicode-escape').replace('\\','')
得到html为:
可以看到,需要的中文内容已经提取完毕,接下来我们需要做的是对我们感兴趣的内容进行提取,,这里我们感兴趣的是每条评论的文本内容、评分以及评论时间,这里使用正则表达式来提取:
import re
from bs4 import BeautifulSoup obj = BeautifulSoup(html,'lxml') # # '''利用findAll定位目标标签及其属性并返回其字符形式结果'''
text = list(obj.findAll('p', {'class': "rev-txt"}))
star = list(obj.findAll('span'))
Time = list(obj.findAll('span', {'class': "time"})) #将每一条评论对应的内容提取出来
control = 0
for m in range(len(star)):
try:
if 'star' in str(star[m]):
'''设置不同的正则规则来提取目标内容'''
print(re.findall('[0-5]+', str(star[m]))[0])
print(re.sub('[a-zA-Z="\-<> \/\n\r]+', '', str(text[control])))
print(re.findall('<.*?>(.*?)<.*?>',str(Time[control]))[0])
control += 1
except Exception as e:
print('error')
通过上面的测试,我们成功获取到该测试页内的所需内容,下面附上完整采集的代码,只是加上一些错误处理机制、随机暂停防ban机制和一些保存数据的内容:
2.6 完整的采集程序
正式采集部分沿用前面测试中的思想,具体代码如下:
'''这个脚本用于对JS脚本控制翻页的动态网页进行爬取''' import requests
import time
import random
from bs4 import BeautifulSoup
import re
import json
import pandas as pd #设置请求头文件
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36',
'Accept':'*/*',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Referer':'http://www.mafengwo.cn/poi/6653.html'
} #设置cookies
cookie = {
'PHPSESSID':'1nivct21bumab1adia6i7k1a82',
'mfw_uuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
'uva':'s%3A78%3A%22a%3A3%3A%7Bs%3A2%3A%22lt%22%3Bi%3A1532508009%3Bs%3A10%3A%22last_refer%22%3Bs%3A6%3A%22direct%22%3Bs%3A5%3A%22rhost%22%3Bs%3A0%3A%22%22%3B%7D%22%3B',
'__mfwurd':'a%3A3%3A%7Bs%3A6%3A%22f_time%22%3Bi%3A1532508009%3Bs%3A9%3A%22f_rdomain%22%3Bs%3A0%3A%22%22%3Bs%3A6%3A%22f_host%22%3Bs%3A3%3A%22www%22%3B%7D',
'__mfwuuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
'UM_distinctid':'164d0987902bb2-0bb3f4e59cf01f-3e3d560e-1fa400-164d098790328c',
'oad_n':'a%3A3%3A%7Bs%3A3%3A%22oid%22%3Bi%3A1029%3Bs%3A2%3A%22dm%22%3Bs%3A15%3A%22www.mafengwo.cn%22%3Bs%3A2%3A%22ft%22%3Bs%3A19%3A%222018-07-25+16%3A40%3A08%22%3B%7D',
'__mfwlv':'',
'__mfwvn':'',
'__mfwlt':'' } '''JS脚本发起的真实的网址请求对应的网址内容模板(及除去几个动态参数之外的死板的url内容)'''
url_left = 'http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApi?callback=jQuery18109093089574473625_1532513669920¶ms=%7B%22poi_id%22%3A%22'
url_middle = '%22%2C%22page%22%3A'
url_right = '%2C%22just_comment%22%3A1%7D&_=1532513718986' data = pd.read_excel(r'C:\Users\windows\Desktop\summer_project\GIS\data\马蜂窝重庆景点评论数据(2018-7-26采集)\chongqing_scene.xlsx') '''读入poi_id数据以在循环中进行url的构建''' Q = {}
poi_id_list = []
scene_name = []
for key,value in zip(data['id'],data['景点名称']):
Q[str(key)] = str(value)
poi_id_list.append(str(key))
scene_name.append(value) comment = []
S = []
id = []
t = [] count = 1
for i in poi_id_list:
print('{}采集开始'.format(Q[i])) '''构造包含poi_id内容的url前半部分内容'''
url_first = url_left+i+url_middle
for j in range(1,10000):
try:
'''构造包含翻页信息的完整url内容'''
url_first = url_left + i + url_middle
url = url_first + str(j) + url_right '''向构造好的真实网页发起请求'''
r = requests.get(url=url, headers=headers, cookies=cookie) '''对相应的网页内容进行转码'''
html = r.content.decode('unicode-escape') '''判断当前景点所有有效评论页面是否已被爬取完成'''
if '暂无内容' in str(html):
print('本景点评论数据已被爬完!')
break
else:
'''将网页内容中的单\替换成空'''
html = html.replace('\\', '')
'''利用bs4对网页内容进行CSS解析'''
obj = BeautifulSoup(html, 'lxml') # # '''利用findAll定位目标标签及其属性并返回其字符形式结果'''
text = list(obj.findAll('p', {'class': "rev-txt"}))
star = list(obj.findAll('span'))
Time = list(obj.findAll('span', {'class': "time"})) '''设置一个复杂周密的错误处理机制以防止长时间爬虫任务中可能出现的各种错误中断任务主体'''
control = 0
for m in range(len(star)):
try:
if 'star' in str(star[m]):
'''设置不同的正则规则来提取目标内容'''
token = re.findall('[0-5]+', str(star[m]))[0]
comment.append(re.sub('[a-zA-Z="\-<> \/\n\r]+', '', str(text[control])))
t.append(re.findall('<.*?>(.*?)<.*?>',str(Time[control]))[0])
S.append(int(token))
id.append(i)
print('-'*100)
print('总第{}条评论被采集'.format(str(count)))
print('-' * 100)
count += 1
control += 1
else:
pass
except Exception as e:
pass '''设置随机睡眠机制以防止被ban'''
print('='*100)
print('{}的'.format(Q[i]),'第{}页被采集完'.format(str(j)))
print('=' *100)
time.sleep(random.randint(2,4))
except Exception as e:
pass
print('{}采集结束'.format(Q[i])) '''写出数据到数据框'''
df = pd.DataFrame({'id':id,
'comment':comment,
'S':S,
'Time':t}) df.to_excel('raw_data.xlsx',index=False)
运行结果:
最终得到的评论数据集格式如下:
以上就是关于本文的全部内容,如有不解之处,望指出。
(数据科学学习手札47)基于Python的网络数据采集实战(2)的更多相关文章
- (数据科学学习手札90)Python+Kepler.gl轻松制作时间轮播图
本文示例代码及数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 Kepler.gl作为一款强大的开源地理信 ...
- (数据科学学习手札32)Python中re模块的详细介绍
一.简介 关于正则表达式,我在前一篇(数据科学学习手札31)中已经做了详细介绍,本篇将对Python中自带模块re的常用功能进行总结: re作为Python中专为正则表达式相关功能做出支持的模块,提供 ...
- (数据科学学习手札33)基于Python的网络数据采集实战(1)
一.简介 前面两篇文章我们围绕利用Python进行网络数据采集铺垫了很多内容,但光说不练是不行的,于是乎,本篇就将基于笔者最近的一项数据需求进行一次网络数据采集的实战: 二.网易财经股票数据爬虫实战 ...
- (数据科学学习手札85)Python+Kepler.gl轻松制作酷炫路径动画
本文示例代码.数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 Kepler.gl相信很多人都听说过,作为 ...
- (数据科学学习手札110)Python+Dash快速web应用开发——静态部件篇(下)
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- (数据科学学习手札136)Python中基于joblib实现极简并行计算加速
本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 我们在日常使用Python进行各种数据计算 ...
- (数据科学学习手札102)Python+Dash快速web应用开发——基础概念篇
本文示例代码与数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的新系列教程Python+Dash快 ...
- (数据科学学习手札108)Python+Dash快速web应用开发——静态部件篇(上)
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- (数据科学学习手札109)Python+Dash快速web应用开发——静态部件篇(中)
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
随机推荐
- vSan中见证组件witness详解
witness在vSan中作为见证组件其作用类似于WinServer中的仲裁磁盘,当Cluster中某一节点发生故障时,来判断该节点上的对象在哪一个新的节点上继续承载.此处需要强调的是,witness ...
- 沉淀,再出发:结合案例看python
沉淀,再出发:结合案例看python 一.前言 关于python,如果不经过大型程序开发的洗礼,我们很难说自己已经懂得了python了,因此,我们需要通过稍微结构化的编程来学习python. 二.一个 ...
- August 14th 2017 Week 33rd Monday
Life is like a watch, you can return to the starting point, they are not yesterday! 人生就像钟表,可以回到起点,却已 ...
- java内部类之成员内部类之局部内部类
局部内部类特点: 1.定义在代码块.方法体内的类叫局部内部类 2.局部内部类访问外部类的属性和方法使用“外部类名.this.属性名”和“外部类名.this.方法名(参数)”的形式 3.对外部世界完全隐 ...
- 简析Chrome和Webkit的渊源
http://www.3lian.com/edu/2012/05-25/28803.html 互联网的浪潮从未停息,而用以网上冲浪的冲浪板也一直在变得愈加精良.自人们进入互联网时代以来,即已经发生了三 ...
- 2763. [JLOI2011]飞行路线【分层图最短路】
Description Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司.该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并 ...
- [CTSC2018]假面
题目 先来考虑一下第一问,血量有\(P\)的概率减\(1\) 由于我们最后需要求每一个人的期望血量,于是考虑维护出每个人处于不同血量时候的概率 一个简单\(dp\)即可 \[dp_{i,j}=dp_{ ...
- 「bzoj 4180: 字符串计数」
题目 真是一道好题 首先根据一个非常显然的贪心,如果给出了一个串\(S\),我们如何算最小操作次数呢 非常简单,我们直接把\(S\)拉到\(T\)的\(SAM\)上去跑,如果跑不动了就停下来,重新回到 ...
- 5、Web Service-整合CXF
1.工程准备 继续使用之前的服务端:https://www.cnblogs.com/Mrchengs/p/10562458.html 2.jar准备 前去apache官网下载响应的jar:http:/ ...
- SpringBoot实战(十一)之与JMS简单通信
什么是JMS? 引用百度百科上的说明: JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之 ...