BB:作为入门爬虫的新手,这些天在网上找一些案例自己动手实现以下并添加自己的东西进去。
这个案例不太复杂,用到的有re、json、requests、pandas库
简单介绍下这几个库的作用:
re(regular expression):它就是正则表达,用来解析html页面的信息。
常用方法:
re.complie(pattern) 一个字符串编译为字节代码,返回一个Pattern object.pattern:你想匹配到的正则表达式,相当于样式;
re.findall(pattern, string) 在指定的字符串中找到要匹配的信息,返回 list列表。string:字符串类型,是你要在哪里进检索;
re.sub(pattern, repl, string) 用repl替换string中的符合pattern表达式的信息。
json(JavaScript Object Notation):使用json模块可以轻松解析包含JSON对象的JSON字符串和文件
json.load:读取包含JSON对象的文件
json.loads:将一个json串还原成python对象
requests:向服务器发送请求
request.get(url,headers)
pandas:数据分析包
pandas.read_csv():读取csv文件
首先明白要爬取些什么内容?
你搜索一个问题时会有很多相关的很多问题,我将问题简化为:获取某一个问题的所有回答者的基本信息以及在该问题下的回答内容。就是这些东西:
怎样才能获取这些信息呢?这些信息都镶嵌在html页面中,在你下滑浏览其他回答时,会不断向服务器发送各种请求,里边有一种是我们比较感兴趣的,那就是jason数据。
1.找到你想要爬取的问题,进入页面,打开开发者模式,刷新页面
2.点击Network,选中XHR
3.在你下滑滚动条看其他回答时,发现不断有request出现,我们要找的数据在name为:answers?...的jason数据中,找不到的话,Ctrl+F,自行查找。
点击后是它的相关信息,其中General中的RequestURL里面的信息是我们想要的,可以把它打开它观察一下。
点击Previews,可以看到包含data,paging两个”大字典”,data中有5个“小字典“,所以说每次会出现5个回答。paging中totals记录的是当前话题下有多少回答。
我们随便打开一个data中的内容,author记录的回答者的信息,有name、gender(1:男,0:女,-1:无)、voteup_count:赞数、comment_count:评论数量。在content中是回答内容。
现在数据的藏身之处找到后,就开始搞他了😏
回到General中的RequestURL打开几个jason数据找找有啥子规律
'https://www.zhihu.com/api/v4/questions/24435989/answers?include=data%5B%2A%5D.is_normal%2Cadmin_limit=5&offset=5&platform=desktop&sort_by=default'
'https://www.zhihu.com/api/v4/questions/24435989/answers?include=data%5B%2A%5D.is_normal%2Cadmin_limit=5&offset=10&platform=desktop&sort_by=default'
'https://www.zhihu.com/api/v4/questions/24435989/answers?include=data%5B%2A%5D.is_normal%2Cadmin_limit=5&offset=15&platform=desktop&sort_by=default'
除offset以外其他信息都一样,offset每次加5。有了规律后,所有包含回答的url就可以得到了。
现在可以搞了。
我把整个过程放在了一个类里边封装了起来,调用时会有交互式的感觉。
#导入相关库
import re
import json
import requests
import pandas as pd
class ZH:
#用于放置每个回答者信息
comments = []
save_path=input('请输入你要保存的地址:')
#获取html页面信息
def getHTML(self,url):
headers = {
'accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
'User-Agent':'Mozilla/5.0 3578.98 Safari/537.36',
'cookie':'_xsrf=rg0NI02ofm8sDZNLgxcKvnGG6NY3A3fv; _zap=8cafe3ea-4a87-49b7-a9cf-8bfe39a0fc15; d_c0="AHDWCm1Z5hCPTqD6pzMzZwCilzhqSVFfBSE=|1583116388"; _ga=GA1.2.28633410.1583116390; _gid=GA1.2.811426709.1583116390; q_c1=46d38f891fe74bf28c9db86250b0ef18|1583116464000|1583116464000; tst=r; capsion_ticket="2|1:0|10:1583119816|14:capsion_ticket|44:ZDgzZmYzZjZlOWMxNDJiY2JmMTIxZWVlN2QzMzJlMTg=|828dd6d9a9f37a32945f63b06748324b980d328c6dda3f1f933de5567c67cb7e"; l_n_c=1; o_act=login; ref_source="other_https://www.zhihu.com/signin?next=/"; r_cap_id="MDVhZjdlZjk3MDAwNGQ3YTlkMDIyNDM4ZWYxOTczYmE=|1583119880|438b6541f5adc1ff895e369b656ba58cc7436fef"; cap_id="MDJlZWQwNjZlZDYzNDAyZjgzY2MxZTVlM2YyMzMxY2Y=|1583119880|39f107b06ca8affea3e36a8acb7d782a0bf3f03e"; l_cap_id="YjczOWM3MDhjYmFjNGViNGEwMmIxOThlZTIyYThkMGE=|1583119880|1beff2b7a8613639597213b23cd23c8499bfe82b"; n_c=1; z_c0="2|1:0|10:1583120005|4:z_c0|92:Mi4xUW9IV0J3QUFBQUFBY05ZS2JWbm1FQ1lBQUFCZ0FsVk5oY3hKWHdBZ0FNV2RXTWV3Rzlsc0d6b1NJRVY4ZTU1LWJn|2db7aacc3e3eb5104bcbe0c99d8acd3b038fbb9cf2cd9e01b3dc34def7bafe3e"; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1583116389,1583119673,1583120201; Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49=1583120201; KLBRSID=81978cf28cf03c58e07f705c156aa833|1583120481|1583115787'
}
# proxy='204.13.155.19'
try:
r=requests.get(url,headers=headers)
r.raise_for_status()
r.encoding="utf-8"
return r.text
except requests.HTTPError as e:
print(e)
print("HTTPError")
except requests.RequestException as e:
print(e)
except:
print("Unknown Error !")
#提取到的评论里边会有其他信息,该方法用于剔除干扰信息
def fixData(self,x):
removeHref = re.compile('<a href.*?</a>')
removeFig = re.compile('<figure.*?/figure>')
replaceB = re.compile('<b>|</b>')
x = re.sub(removeHref, "", x)
x=re.sub(replaceB,'',x)
x=re.sub(removeFig,'',x)
return x.strip()
#得到jason数据的内容
def getData(self,html):
json_data=json.loads(html)['data']
try:
for item in json_data:
comment=[]
#用户名称
comment.append(item['author']['name'])
#用户性别
comment.append(item['author']['gender'])
#赞同数量
comment.append(item['voteup_count'])
#评论数量
comment.append(item['comment_count'])
#回答内容
raw=re.findall('<p>(.*?)</p>',item['content'])
for i in range(len(raw)):
raw[i]=self.fixData(raw[i])
comment.append(raw)
print('正在保存 %s 的信息......'%item['author']['name'])
self.comments.append(comment)
except:
return 'getData Error'
#当所有信息填充到comments中后,保存在本地
def save_data(self):
my_path=self.save_path
dataframe=pd.DataFrame(self.comments)
dataframe.to_csv(my_path,header=False,index=False,sep=',',mode='w',encoding='utf_8_sig')
#开始函数
def start(self):
url = 'https://www.zhihu.com/api/v4/questions/375331442/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%2Cis_recognized%2Cpaid_info%2Cpaid_info_content%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset=0&platform=desktop&sort_by=default'
html=self.getHTML(url)
total_answer_num=json.loads(html)['paging']['totals']
print("本问题回答数共有%d条"%(total_answer_num))
page=0
self.comments.append(['名字', '性别', '赞数', '评论数', '回答内容'])
#total_answer_num可替换为你想要的个数(小于total_answer_num)
while(page<total_answer_num):
url = 'https://www.zhihu.com/api/v4/questions/375331442/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%2Cis_recognized%2Cpaid_info%2Cpaid_info_content%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset={}&platform=desktop&sort_by=default'.format(str(page))
html=self.getHTML(url)
self.getData(html)
page+=5
self.save_data()
print("爬取数据完成^-^!")
zhihu=ZH()
zhihu.start()
结果来了:
这么多数据部不分析一下就可惜,继续搞他....
## 1、Import libraries
import pandas as pd
import matplotlib.pyplot as plt
## 2、Import data
data=pd.read_csv('./data/zhihu.csv')
data.head()
## 3、check dataset
data.info()
data.describe()
#最多赞数为:1217,最多评论数为241
#字符串型数据的信息:
data.describe(include=['O'])
## 4、Analyze data
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
data.groupby('性别').count()
#男生167 女生37 未知37
vs=data[['性别','赞数']].groupby('性别').sum().sort_values(by='赞数',ascending=False)
vs
vs.plot(kind='pie',subplots=True,autopct = '%3.2f%%',fontsize=15)
#男生获赞最多
#Top5 的回答者
data.sort_values(by='赞数',ascending=False)[:5]
也只能做个频数分析了内容的好评坏评目前还不做不了,那属于机器学习的范畴了。
对爬虫有兴趣的小伙伴儿可以试试这个例子,对数据分析感兴趣的话可以自己试着分析一下。
我用不同方法预测泰坦尼克号的生还情况的源码在Github:https://github.com/guodalongplus/Titanic-Data-Science-Solutions