帖子网页分析
- 帖子导航
<span class="pagernums" data-pager="list,meigu_|1706|80|1">
从这个标签中可以获得帖子总数1706,以及每一页帖子的数量80,当前处于第几页:第一页。
![美股吧帖子列表网页分析](http://upload-images.jianshu.io/upload_images/5298387-ca563fc7a0c2552e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 构造帖子列表的url
http://guba.eastmoney.com/list,meigu_2.html
帖子列表的url可以表示为:
'http://guba.eastmoney.com/list,meigu_{}.html'.format(page_num)
可以根据帖子总数/每页帖子的数量得到一个帖子url的列表,代码表示:
page_data = soup.find(name='span', class_='pagernums').get('data-pager').split('|')
page_nums = math.ceil(int(page_data[1]) / int(page_data[2]))
**注意:使用math模块的ceil函数向上取整**
- 循环获取每一页帖子的信息
## 评论网页分析
- 评论页导航
> 查看网页的html信息,查询105,有三个地方可以获取到这个信息,这里用了正则表达式从script中获取。
{var num=40030; }var pinglun_num=105;var xgti="";if(typeof (count) != "undefined"){xgti="<span class="tc2"><a href='list,meigu.html'>相关帖子<span class="tc1">"+count+"</span>条</a></span>";}
![评论页导航信息](http://upload-images.jianshu.io/upload_images/5298387-8e7eae194d70eb22.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 构造评论页url
http://guba.eastmoney.com/news,meigu,613304918_2.html
帖子评论url可以表示为:
'http://guba.eastmoney.com/news,meigu,613304918_{}.html'.format(page_num)
可以根据评论总数reply_count / 30(有分页情况下,每页帖子的数量最多为30)得到一个帖子url的列表,代码表示:
pattern = re.compile(r'var pinglun_num=(.*?);')
文章评论数
reply_count = int(re.search(pattern, resp.text).group(1))
page_num = math.ceil(reply_count / 30)
**注意:使用math模块的ceil函数向上取整**
- 循环获取每一页评论的信息
先判断有没有评论,如果有的话遍历评论url,返回帖子的评论信息
## 使用的库
- requests:发起网页请求
- BeautifulSoup:解析网页
- re:正则表达式解析网页
- math:使用ceil函数向上取整
- csv:数据保存为csv文件
## 爬取过程
1. 以http://guba.eastmoney.com/list,meigu.html为入口;
2. 先获取帖子的总数、计算出帖子导航页的页码数;
3. 得到帖子的导航url列表;
4. 遍历帖子的导航url,得到帖子的信息;
- 遍历帖子url的地址,得到帖子的阅读量、评论数、标题
- 获取评论信息
- 以帖子url,如http://guba.eastmoney.com/news,meigu,646708357.html 为入口
- 先获取评论的总数,计算出帖子评论的页数
- 得到评论导航的url列表
- 遍历评论url列表,得到帖子的评论信息
## 代码
import requests
from bs4 import BeautifulSoup
import math
import re
import csv
start_url = 'http://guba.eastmoney.com/list,meigu_1.html'
url = "http://guba.eastmoney.com/news,meigu,646708357.html"
base_url = "http://guba.eastmoney.com"
获取所有帖子的信息
def get_articles_info(start_url):
resp = get_html(start_url)
soup = BeautifulSoup(resp.text, 'html.parser')
page_data = soup.find(name='span', class_='pagernums').get('data-pager').split('|')
page_nums = math.ceil(int(page_data[1]) / int(page_data[2]))
print('共{}页'.format(page_nums))
articles_infos = []
with open('meigu.csv', 'a') as csv_file:
writer = csv.writer(csv_file)
writer.writerow(['阅读量', '评论数', '发布时间', '帖子网址', '帖子标题', '帖子评论'])
for i in range(1, page_nums+1):
print('爬取第{}页...'.format(i))
articles_url = start_url.split('')[0] + '' + str(i) + '.html'
articles_infos = parser_articles_info(articles_url)
articles_infos.extend(articles_infos)
return articles_infos
获取一页的所有帖子信息:阅读量、评论数、发布时间、帖子的url、帖子的标题、帖子的所有评论
param:每一页帖子的链接
def parser_articles_info(article_list_url):
resp = get_html(article_list_url)
articles_soup = BeautifulSoup(resp.text, 'html.parser')
articles_infos = articles_soup.find_all(name='div', class_='articleh')
articles = []
for info in articles_infos:
if '/news' in info.find(name='span', class_='l3').find(name='a').get('href'):
article_infos = {
'read_count': info.find(name='span', class_='l1').text,
'reply_count': info.find(name='span', class_='l2').text,
'release_time': info.find(name='span', class_='l5').text,
'article_url': base_url + info.find(name='span', class_='l3').find(name='a').get('href'),
'article_title': info.find(name='span', class_='l3').find(name='a').get('title'),
'article_comments': parse_comment_page(get_html(base_url + info.find(name='span', class_='l3').find(name='a').get('href')))
}
with open('meigu.csv', 'a') as csv_file:
writer = csv.writer(csv_file)
writer.writerow(article_infos.values())
articles.append(article_infos)
# print(articles)
return articles
根据url获取html文档
def get_html(url):
headers={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
}
resp = requests.get(url)
if resp.status_code == 200:
return resp
return None
解析帖子的html文档,提取需要的数据:帖子的内容以及帖子的所有评论
def parse_comment_page(resp):
soup = BeautifulSoup(resp.text, 'html.parser')
# 正则表达式获取总的评论数
pattern = re.compile(r'var pinglun_num=(.*?);')
article_info = {}
# 文章评论数
article_info['reply_count'] = int(re.search(pattern, resp.text).group(1))
# 文章内容
article_info['article_content'] = soup.find(name='div', class_='stockcodec').text.strip()
# print(article_info['article_content'])
page_num = math.ceil(article_info['reply_count'] / 30)
print('{}条评论'.format(article_info['reply_count'] ), ',', '共{}页'.format(page_num))
# 爬取所有的评论
article_comments = []
if article_info['reply_count'] > 0:
for i in range(1, page_num+1):
comment_url = '.'.join(resp.url.split('.')[:-1]) + '_{}'.format(i) + '.html'
print(comment_url)
article_comments.extend(parser_article_comment(comment_url))
else:
article_comments.append('本帖子暂时没有评论内容')
return article_comments
获得帖子一页的评论信息
def parser_article_comment(comment_list_url):
resp = get_html(comment_list_url)
if resp:
comment_soup = BeautifulSoup(resp.text, 'html.parser')
comments_infos = comment_soup.find_all(name='div', class_='zwlitxt')
comments = []
# print(len(comments_infos))
for info in comments_infos:
comment = {}
comment['commentator'] = info.find(name='span', class_='zwnick').find('a').text if info.find(name='span', class_='zwnick').find('a') else None
comment['reply_time'] = info.find(name='div', class_='zwlitime').text
comment['reply_content'] = info.find(name='div', class_='zwlitext').text
comments.append(comment)
return comments
def main():
get_articles_info(start_url)
if name == 'main':
main()
## 运行结果
![爬取的结果](http://upload-images.jianshu.io/upload_images/5298387-f98fdbc345c75d57.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
> 爬虫能正常运行,但是爬取的过程很慢