Python高级应用程序设计任务要求

用Python实现一个面向主题的网络爬虫程序,并完成以下内容:
(注:每人一题,主题内容自选,所有设计内容与源代码需提交到博客园平台)

一、主题式网络爬虫设计方案(15分)
1.主题式网络爬虫名称

  今日热榜数据爬取  地址:https://tophub.today/
2.主题式网络爬虫爬取的内容与数据特征分析

  爬取微博,百度,知乎三个榜单和历史榜单的标题与热度。
3.主题式网络爬虫设计方案概述(包括实现思路与技术难点)

  获取到三个平台的链接,构造headers请求头请求网页,使用lxml解析网页,通过观察源码结构提取出热门话题和对应的热度值。

请求需要根据控制台监控的请求模拟构造请求头。

每个平台的热度值表示方式不同,无法使用统一的处理方式获取到其中的数值,最终决定使用正则匹配到数值。

对标题进行文本分析,使用结巴分词提取关键字并且画出词云。

二、主题页面的结构特征分析(15分)
1.主题页面的结构特征

  主题页面为多家平台的榜单数据,选取综合平台中的三家进行爬取。

每家平台的榜单分为当前榜单和历史榜单。

2.Htmls页面解析

  两个榜单分别在两个class=table的table中,每条数据都在table中的一个tr里。
3.节点(标签)查找方法与遍历方法
(必要时画出节点树结构)

  首先找到两个table,然后遍历table,找到tr,然后遍历tr,第二个td里的a标签中存在着文本,第三个td的文本中有热度值。

三、网络爬虫程序设计(60分)
爬虫程序主体要包括以下各部分,要附源代码及较详细注释,并在每部分程序后面提供输出结果的截图。
1.数据爬取与采集

  先使用字典遍历下三个平台的名称和对应的url,然后构造一个请求头,通过F12打开控制台观察请求头,挑取其中的user-agent,accent,cookie进行构造。

将请求的字符串文本使用lxml的etree构造成dom对象,使用xpath进行提取内容。

最终将提取的内容保存到csv。

代码如下:

import requests
from lxml import etree
import pandas as pd
'''
网址:https://tophub.today/
微博:https://tophub.today/n/KqndgxeLl9
知乎:https://tophub.today/n/mproPpoq6O
百度:https://tophub.today/n/Jb0vmloB1G
'''

urls = {
    '微博': 'https://tophub.today/n/KqndgxeLl9',
    '知乎': 'https://tophub.today/n/mproPpoq6O',
    '百度': 'https://tophub.today/n/Jb0vmloB1G',
}

headers = {
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'cookie': 'UM_distinctid=16ef50dda3e9c1-0fe2c80daba785-2393f61-1fa400-16ef50dda3fa9a; Hm_lvt_3b1e939f6e789219d8629de8a519eab9=1576069363,1576902795; CNZZDATA1276310587=1873865941-1576066676-%7C1576904182; Hm_lpvt_3b1e939f6e789219d8629de8a519eab9=1576906643',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}

for platform, url in urls.items():
    data = []
    response = requests.get(url, headers=headers)
    selectore = etree.HTML(response.text)
    tables = selectore.xpath('.//table[@class="table"]')
    for table in tables:
        trs = table.xpath('.//tr')
        for tr in trs:
            title = tr.xpath('./td[2]/a/text()')[0]
            number = tr.xpath('./td[3]/text()')[0]
            data.append([title, number])
    df = pd.DataFrame(data, columns=['title', 'number'])
    df.to_csv('data/{}.csv'.format(platform), index=False)
 
 


2.对数据进行清洗和处理

对三个平台的热度值进行处理,每个平台的热度值表达方式不同,因此选择用正则提取每个热度值里的数值。使用 \d+.?\d?提取。
对标题使用百度人工智能平台的文本分类进行分类,从而可以分析当今互联网热门关注的分类有哪些。
代码如下:
import json
import time
import re

import pandas as pd
from aip import AipNlp

# """ 你的 APPID AK SK """
APP_ID = '11703996'
API_KEY = '5TetNdgFFeWQxNbI8H4fZ8kq'
SECRET_KEY = 'RWmPGTBdcStIiDMtb7GOGGB0PVXMCyce'

""" 你的 APPID AK SK """
APP_ID = ''
API_KEY = ''
SECRET_KEY = ''

client = AipNlp(APP_ID, API_KEY, SECRET_KEY)


for platform in ['百度', '微博', '知乎']:
    df = pd.read_csv('data/{}.csv'.format(platform))
    # total = df['handle_number'].sum()
    # df['popularity'] = [round(popularity/total*1000, 3) for popularity in baidu_df['popularity']]

    df['handle_number'] = 0.0
    df['lv1_tag'] = ''
    for index, row in df.iterrows():
        print(index)
        try:
            title = row['title']
            number = row['number']

            find = re.findall('\d+.?\d?', number)
            if find:
                df.loc[index, 'handle_number'] = float(find[0])
            else:
                pass
            content = title * 20
            """ 调用api """
            topic_result = client.topic(title, content)
            lv1_tag_list = topic_result.get('item').get('lv1_tag_list')
            if lv1_tag_list:
                lv1_tag = lv1_tag_list[0].get('tag')
                df.loc[index, 'lv1_tag'] = lv1_tag
        except:
            pass
    df = df.sort_values('handle_number', ascending=False)
    df= df.reset_index(drop=True)
    df.to_csv('./handle_data/{}.csv'.format(platform), index=None)
 

3.文本分析(可选):jieba分词、wordcloud可视化

将每个平台的标题进行汇总,加载停用词后使用jieba第三方包进行关键词提取,将提取的关键字和相应权重使用 wordcloud进行可视化词云。

代码如下:

import jieba.analyse
import pandas as pd
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import random

# 中文乱码和坐标轴负号的处理
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# df = None
for platform in ['百度', '微博', '知乎']:
    df = pd.read_csv('./handle_data/{}.csv'.format(platform))

    # 热度排行榜top10
    df = df.sort_values('handle_number', ascending=False)
    df= df.reset_index(drop=True)
    fig = plt.figure(figsize=(15, 6))
    top10_df = df[:10]
    x = list(top10_df.index)
    y = list(top10_df['handle_number'].values)
    titles = list(top10_df['title'].values)
    plt.barh(titles, y)
    fig.tight_layout()
    for x_, y_, title in zip(x, y, titles):
        plt.text(y_+50 , title, '%.f' % y_, ha='center', va='bottom')
    plt.savefig('{}热度排行榜top10.png'.format(platform))
    plt.close()

    # 分类占比
    lv1_tag = df['lv1_tag'].value_counts(normalize=True)
    lv1_tag.plot.pie(subplots=True, figsize=(16, 16), label='分类占比') # autopct="%1.2f%%"
    plt.savefig('{}分类占比图.png'.format(platform))
    plt.close()

    # 分类热度
    lv1_tag = df.groupby('lv1_tag')['handle_number'].sum()
    lv1_tag = lv1_tag.sort_values()
    lv1_tag.plot('bar', figsize=(10, 6))
    plt.xticks(rotation=60)
    plt.savefig('{}分类热度图.png'.format(platform))
    plt.close()

    # jieba词云
    df.loc[:, 'title'] = df['title'].astype('str')
    content = list(df['title'].values)
    content = ''.join(content)
    try:
        jieba.analyse.set_stop_words('./data/stopwords.txt')
        tags = jieba.analyse.extract_tags(content, topK=100, withWeight=True)
        keywords = dict()
        for i in tags:
            keywords[i[0]]=i[1]
        wc = WordCloud(
                    font_path='/usr/share/fonts/winfonts/simfang.ttf',
                    background_color='White',
                    max_words=1000,
                    width=1000,
                    height=500,
                    #mask=graph,
                    scale=1,
                    )
        wc.generate_from_frequencies(keywords)#按词出现的频率
        wc.to_file("{}词云.jpg".format(platform))
    finally:
        pass

结果如下:


4.数据分析与可视化
(例如:数据柱形图、直方图、散点图、盒图、分布图、数据回归分析等)

(1)各平台热度排行榜top10

  根据个平台的热度进行排序,选取前10名画出横向柱形图。直观看出各平台当前最火的话题。

  代码如下:

    df = df.sort_values('handle_number', ascending=False)
    df= df.reset_index(drop=True)
    fig = plt.figure(figsize=(15, 6))
    top10_df = df[:10]
    x = list(top10_df.index)
    y = list(top10_df['handle_number'].values)
    titles = list(top10_df['title'].values)
    plt.barh(titles, y)
    fig.tight_layout()
    for x_, y_, title in zip(x, y, titles):
        plt.text(y_+50 , title, '%.f' % y_, ha='center', va='bottom')
    plt.savefig('{}热度排行榜top10.png'.format(platform))
    plt.close()

(2)各平台话题分类占比

  统计各平台分类占比,对比每个平台对当今社会的侧重点在哪。

  代码如下:

 
      lv1_tag = df['lv1_tag'].value_counts(normalize=True)
      lv1_tag.plot.pie(subplots=True, figsize=(16, 16), label='分类占比') # autopct="%1.2f%%"
      plt.savefig('{}分类占比图.png'.format(platform))
      plt.close()

(3)各平台分类热度总值柱形图

  使用pandas对各平台的话题分类进行分组计算热度总值,通过数据直观的看出各平台对当今社会的热点关注。

  代码如下:

      lv1_tag = df.groupby('lv1_tag')['handle_number'].sum()
      lv1_tag = lv1_tag.sort_values()
      lv1_tag.plot('bar', figsize=(10, 6))
      plt.xticks(rotation=60)
      plt.savefig('{}分类热度图.png'.format(platform))
      plt.close()
 

5.数据持久化

爬取数据和处理数据均使用csv保存


6.附完整程序代码

import requests
from lxml import etree
import pandas as pd
'''
网址:https://tophub.today/
微博:https://tophub.today/n/KqndgxeLl9
知乎:https://tophub.today/n/mproPpoq6O
百度:https://tophub.today/n/Jb0vmloB1G
'''

urls = {
    '微博': 'https://tophub.today/n/KqndgxeLl9',
    '知乎': 'https://tophub.today/n/mproPpoq6O',
    '百度': 'https://tophub.today/n/Jb0vmloB1G',
}

headers = {
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'cookie': 'UM_distinctid=16ef50dda3e9c1-0fe2c80daba785-2393f61-1fa400-16ef50dda3fa9a; Hm_lvt_3b1e939f6e789219d8629de8a519eab9=1576069363,1576902795; CNZZDATA1276310587=1873865941-1576066676-%7C1576904182; Hm_lpvt_3b1e939f6e789219d8629de8a519eab9=1576906643',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}

for platform, url in urls.items():
    data = []
    response = requests.get(url, headers=headers)
    selectore = etree.HTML(response.text)
    tables = selectore.xpath('.//table[@class="table"]')
    for table in tables:
        trs = table.xpath('.//tr')
        for tr in trs:
            title = tr.xpath('./td[2]/a/text()')[0]
            number = tr.xpath('./td[3]/text()')[0]
            data.append([title, number])
    df = pd.DataFrame(data, columns=['title', 'number'])
    df.to_csv('data/{}.csv'.format(platform), index=False)


import json
import time
import re

import pandas as pd
from aip import AipNlp

# """ 你的 APPID AK SK """
APP_ID = '11703996'
API_KEY = '5TetNdgFFeWQxNbI8H4fZ8kq'
SECRET_KEY = 'RWmPGTBdcStIiDMtb7GOGGB0PVXMCyce'

""" 你的 APPID AK SK """
APP_ID = ''
API_KEY = ''
SECRET_KEY = ''

client = AipNlp(APP_ID, API_KEY, SECRET_KEY)


for platform in ['百度', '微博', '知乎']:
    df = pd.read_csv('data/{}.csv'.format(platform))
    # total = df['handle_number'].sum()
    # df['popularity'] = [round(popularity/total*1000, 3) for popularity in baidu_df['popularity']]

    df['handle_number'] = 0.0
    df['lv1_tag'] = ''
    for index, row in df.iterrows():
        print(index)
        try:
            title = row['title']
            number = row['number']

            find = re.findall('\d+.?\d?', number)
            if find:
                df.loc[index, 'handle_number'] = float(find[0])
            else:
                pass
            content = title * 20
            """ 调用api """
            topic_result = client.topic(title, content)
            lv1_tag_list = topic_result.get('item').get('lv1_tag_list')
            if lv1_tag_list:
                lv1_tag = lv1_tag_list[0].get('tag')
                df.loc[index, 'lv1_tag'] = lv1_tag
        except:
            pass
    df = df.sort_values('handle_number', ascending=False)
    df= df.reset_index(drop=True)
    df.to_csv('./handle_data/{}.csv'.format(platform), index=None)

import jieba.analyse
import pandas as pd
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import random

# 中文乱码和坐标轴负号的处理
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# df = None
for platform in ['百度', '微博', '知乎']:
    df = pd.read_csv('./handle_data/{}.csv'.format(platform))

    # 热度排行榜top10
    df = df.sort_values('handle_number', ascending=False)
    df= df.reset_index(drop=True)
    fig = plt.figure(figsize=(15, 6))
    top10_df = df[:10]
    x = list(top10_df.index)
    y = list(top10_df['handle_number'].values)
    titles = list(top10_df['title'].values)
    plt.barh(titles, y)
    fig.tight_layout()
    for x_, y_, title in zip(x, y, titles):
        plt.text(y_+50 , title, '%.f' % y_, ha='center', va='bottom')
    plt.savefig('{}热度排行榜top10.png'.format(platform))
    plt.close()

    # 分类占比
    lv1_tag = df['lv1_tag'].value_counts(normalize=True)
    lv1_tag.plot.pie(subplots=True, figsize=(16, 16), label='分类占比') # autopct="%1.2f%%"
    plt.savefig('{}分类占比图.png'.format(platform))
    plt.close()

    # 分类热度
    lv1_tag = df.groupby('lv1_tag')['handle_number'].sum()
    lv1_tag = lv1_tag.sort_values()
    lv1_tag.plot('bar', figsize=(10, 6))
    plt.xticks(rotation=60)
    plt.savefig('{}分类热度图.png'.format(platform))
    plt.close()

    # jieba词云
    df.loc[:, 'title'] = df['title'].astype('str')
    content = list(df['title'].values)
    content = ''.join(content)
    try:
        jieba.analyse.set_stop_words('./data/stopwords.txt')
        tags = jieba.analyse.extract_tags(content, topK=100, withWeight=True)
        keywords = dict()
        for i in tags:
            keywords[i[0]]=i[1]
        wc = WordCloud(
                    font_path='/usr/share/fonts/winfonts/simfang.ttf',
                    background_color='White',
                    max_words=1000,
                    width=1000,
                    height=500,
                    #mask=graph,
                    scale=1,
                    )
        wc.generate_from_frequencies(keywords)#按词出现的频率
        wc.to_file("{}词云.jpg".format(platform))
    finally:
        pass

四、结论(10分)
1.经过对主题数据的分析与可视化,可以得到哪些结论?

  通过分析可知,各平台的话题分类中,社会分类均占比最高,随后微博平台的话题偏娱乐,知乎平台的话题偏教育,百度平台偏娱乐和体育。

通过关键词可以,当前最火的关键词为考研, 微博热点词还有圣诞,李云迪,澳门等,百度的热点词还有唐一菲,道歉,国足等,知乎使用最多

的关键词为评价,看待,是知乎平台常用的问题当时,其次有腾讯,综艺,余年。各平台关注最多的还是社会时事,然后每个平台都有自己关注的

侧重点。

2.对本次程序设计任务完成的情况做一个简单的小结。

  

 本次程序设计,主要学习了伪造headers头请求数据,使用xpath提取数据,对于没有规则的数据,还是需要使用最基础的正则去处理。

pandas和matplotlib主要用于处理,保存数据和画图,画图时对于如何显示出完整的观赏性较高的图片较有难度,比如话题文本较长时,

无法显示出来,就需要新建画图的时间,使用tight_layout进行自适应。

对文本的分类选用了当下较火的百度人工智能平台提供的文本分类功能,从而可以统计各平台的话题分类。分词没有使用jieba的词频
统计,而是使用关键字提取,加载停用词过滤停用词,提取时可以选取指定词性,从而得出关键词和权重,依据此画出词云。
 本次课设对我的python代码能力提升很多,并且学习了多个第三方包的使用方法,受益匪浅。
12-15 16:03