合肥工业大学python大作业之爬虫(手把手教你爬取微博热搜)

Posted 少๑渊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了合肥工业大学python大作业之爬虫(手把手教你爬取微博热搜)相关的知识,希望对你有一定的参考价值。

一、题目要求

二、题目分析

        1、对爬虫的概述

       

 2、爬取信息的基本流程

         不管是通用的网络爬虫还是聚焦性爬虫,其实爬取网页并且获取信息大概按顺序分为以下一些步骤

        (1)选择你想要爬取的网站的网址,和想要的信息(例如图片或者文字或者音频等)

        (2)获取User-Agent,它的作用是将爬虫伪装成浏览器发送信息,让被爬取的网站认为我们是用户的主观点击,而不是一个程序运行的结果。

        (3)通过request获取url,从而得到网页源码,然后在源码中查找数据。

        (4)获取网页响应,这里很重要,也要注意反爬。

        (5)通过url获取网页源代码,然后通过正则表达式获取所需要的信息

        (6)保存获取的信息

       

3、爬虫的准备流程

        在爬虫中我们常用的库有BeautifulSoup库(靓汤),它的作用是解析网页并且获取数据;还有re库,它的作用是对正则表达式的内容进行文字匹配;还有urlib下的request和error,可以定制url获取网页源码;最后是xlwt或者matplotlib等库,用于对保存的数据进行可视化处理。

       如果您使用的是Pycharm,那么下载这些库可以通过以下步骤:在pycharm下依次点击File-->Settings-->Project Interpreter然后可以见到以下界面

     

   如果您没有安装Pycharm,那么可以按下win+R键,输入cmd,在打开的窗口中输入“pip install 库名”即可等待它安装成功。

from bs4 import BeautifulSoup  # 网页解析,获取数据
import re  # 正则表达式,进行文字匹配`
import urllib.request, urllib.error  # 制定URL,获取网页数据
import xlwt  # 进行excel操作
import matplotlib.pyplot as plt

     

  4、具体实现过程

       为了方便理解,我将代码分成了几个部分:主函数部分、编写正则表达式部分、获取URL部分、获取所需信息部分和保存信息或者实现可视化部分

        (1)在主函数中,我们首先需要给定想要爬取的网址,具体方法略,直接搜即可,在这里因为题目要求,我们按照爬取微博为例(主要爬取热搜排名、热搜标题、热搜链接、热搜热度值)。然后通过getData方法获取储存着信息的二维列表;其实在getData里也调用了获取网页源代码的askURL的方法,最后指定储存路径并通过saveData储存到Excel中或者通过其他可视化方法呈现。

def main():
    baseurl = "https://s.weibo.com/top/summary?Refer=top_hot&topnav=1&wvr=6"
    datalist = getData(baseurl)
    savepath = "D:\\PyCharm Community Edition 2020.2.3\\微博热搜50.xls"
    saveData(datalist,savepath)

         

(2)然后所需要做的是编写正则表达式:

        为什么要写正则表达式呢?是因为在我们通过url获取到的网页源代码里有着几千行代码,但是我们所需要的仅仅是一部分,所以正则表达式其实是一种筛选标准,筛选我们所需要的部分。那么什么又是正则表达式呢?这里我不细讲,给出几张图片和一个链接,各位看官自行查阅:正则表达式

 

 

        

         对于本题而言,所需要获取的是热搜排名(其实可有可无,因为是按排名读取,只要在最后写入的时候加上去就行)、链接、标题、热度值。

        那么既然上面说了,正则表达式是一种筛选标准,筛选满足条件的数据,那么我们起码要先观察一下有哪些数据吧?

        打开微博热搜:https://s.weibo.com/top/summary?Refer=top_hot&topnav=1&wvr=6

        右键查看源代码:

         

        得到这么一串花里胡哨的东西,我们忍着性子找找看有没有我们需要的内容:

     

           哎,发现了。这些不就是我要的数据吗?为啥就是这些呢?你跟热搜榜比对一下不就行了...

        那么我们一个个来,观察一下所有热搜链接的共同点:

        对,就是都在<a href=" " target="_blank">这半个标签里面。唯一的不同就是href中的内容,毕竟是不同的链接嘛。所以我们获取链接的正则表达式就是

findLink = re.compile(r'<a href="(.*?)" target="_blank">')

        其中的.*?指获取任意字符0次或者多次尽可能少,也就是不管里面链接多长,都可以获取。

        观察上面的标题所在位置<a>标题</a>,在两个a标签之前。但是我们发现不仅仅标题不同,这里面还包含着热搜嘞。所以我们完全可以通过

findTitleAndLink = re.compile(r'<a href="(.*?)" target="_blank">(.*?)</a>')

同时获取链接和标题,岂不美哉!获取的数据第一个是链接,第二个是标题。

        获取热度的就更简单了,热度值在<span></span>标签中。直接重复.*?就可以了吗?如果这样就错误了,因为细看热搜,就会发现,有的热搜热度除了数值外,还有:

音乐、剧集等汉字,并且汉字和数字之间还有一个空格,你可以选择直接舍弃汉字和空格部分,也可以像我一样包含他们:

findHot = re.compile(r'<span>(\\w*\\s\\d*)</span>')

         

        (3)得到一个指定网页的内容

        在这个方法里,我们一定要做的一件事就是设置head。因为我们访问网页并点击进去就是一个完整的浏览过程,当我们想要通过程序获取的时候,必须要模拟这种过程,不然浏览器会拒绝我们接入,而head就是帮助我们模拟这种过程的工具。

        我们通过模拟浏览器头部信息,向微博服务器发送消息,一般模拟谷歌浏览器

  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73'

        但其实直接就这样就设置完成的话,最终爬取到的是Sina Visitor System 。嗯,应该是被反爬了,咋办呢?还可以通过添加cookie:打开热搜网页,F12进入开发者工具

        

选择 文档,就可以查看到cookie内容,赋值粘贴到head里即可。为啥加上cookie就可以爬取了?因为cookies就是某些网站为了识别用户身份、进行会话跟踪而储存在用户本地终端上的数据,设置好cookies可以模拟用户操作,让浏览器“放下戒心”。具体解释如下:

       

        设置好head之后,通过urllib的request传入爬取的网页和head值,获取到request。

         然后通过urllib.request.urlopen返回一个类型是http.client.HTTPResponse的response对象。最后用一个字符串html储存response解码后的内容,注意解码形式为utf-8.最后返回html就可以获取到网页源代码的字符串形式了。

def askURL(url):
    head = {  # 模拟浏览器头部信息,向服务器发送消息
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73',
         'referer': 'https://s.weibo.com/top/summary?Refer=top_hot&topnav=1&wvr=6',
        'cookie':'SINAGLOBAL=5941149481278.592.1604320169618; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WF9AA32bgUYQHiXEuuVxxJR5JpX5KMhUgL.Fo-4So.ESoMfe0B2dJLoI0qLxK-L1-zLB.BLxKqLBo5L1K2LxK-L1h2LBoqLxK-L1heL12qLxKMLBoBLB-zLxKqL1--LB-zt; ALF=1667371481; SSOLoginState=1635835482; SCF=AkN12N2iMhFTeM7jKDJgpYcYOf5kmH3Ce06tDJU2y2mljYlKd05KkJok_NVeQQlKS5DVQ3MLyNAD0Dq6QEWbaWs.; SUB=_2A25MhK4KDeRhGeNH7VsT9inJyDiIHXVv85jCrDV8PUNbmtANLWLukW9NSp1gXY7kU378-JL7Yek2RvC4p8_BqxCH; _s_tentry=weibo.com; Apache=683258631837.9574.1635836312586; ULV=1635836312593:18:1:1:683258631837.9574.1635836312586:1633605575935; UOR=www.hfut.edu.cn,widget.weibo.com,www.cnblogs.com',

    }
    request = urllib.request.Request(url, headers=head)
    html = ""
    try:
        response = urllib.request.urlopen(request)
        html = response.read().decode("utf-8")
    except urllib.error.URLError as e:
        if hasattr(e, "code"):
            print(e.code)
        if hasattr(e, "reason"):
            print(e.reason)
    return html

       

        (4)获取数据并储存

        定义一个空二维列表储存信息,然后通过BeautifulSoup传入获取的字符串类型源代码html和解析器类型html.parser。然后通过soup的find_all方法找到所有符合条件的数据,进行遍历。

        这次还要再回顾这个图:

       

        细看知道,不管是热搜标题还是链接还是热度,都包含在td标签里,那么我们可以在find_all中指定搜索类型'td',并且为了缩小范围,我们可以加上td的class属性,可以看到源代码中class的属性值td-02。之后就可以开始遍历了。

        在每次遍历中都定义一个一维空列表,将信息依次放入。对了,因为刚刚将html转成了其他类型,所以循环时记得转回字符串,然后通过正则表达式获取信息依次append进行就行了。这里需要注意的一个坑是:微博可能有置顶的,那个是不在50条热搜之内的,但是它也是新闻,我们可以也将它放入,最后输出的时候告诉用户这个是置顶的就行;还有一个坑是,现在双十一快到了,经常有广告掺杂在热搜里面,很烦,因为广告的代码和热搜不太一样,正则表达式识别到td但是识别不出需要的内容就会卡死在那。

我们可以每次读取到它的时候,都直接跳过,毕竟谁想看广告呢你说是吧。然后在每次循环结束的时候将一维列表放入二维列表即可。那么一来二去,咱不就保存了所有需要的信息了吗?

        ps:可以通过APScheduler库定时爬取信息并且更新热搜内容,时间问题先不详述,寒假或者其他时候有时间再具体写。

        (5)可视化保存数据

        我们已将数据全部保存到二维列表中了,那么下一步很显然就是保存。最大众的方法就是写一个txt文件,然后一行一行将信息存进去;稍微升级一点就是将信息储存到Excel里;再高阶一点就是通过matplotlib和numpy库将二维列表转化成二维numpy数组并且以直方图的形式呈现出来;再高阶一点就是实时更新热搜排名并且以直方图的形式显示出来,结果看起来像是动画一般有动态效果。

        还是那句话,快要考试了,实在没时间,只写了保存到Excel,其余可视化方案(直方图、饼图)以后再说。具体保存操作没有难度,就是新建或打开一个Excel,逐行写入即可。

        最近又抽了点时间做了一个直方图可视化,因为50条热搜 太多了,画在图上十分不美观,我就选择了cnt条数据进行展示(cnt由用户自己决定)。代码如下

def draw(datalist):
    x=[]# 保存标题
    y=[]# 保存热度
    cnt=0 # 因为我们在二维列表里存了五十个,所以现在由用户决定我们拿多少出来画图
    for i in datalist:
        if i[2]=='置顶':# 制定无热度,跳过
            continue
        cnt+=1
        if cnt>20:# 这里我设置的是展示20条热搜
            break
        x.append(i[1])
        # 当时挖了个坑,就是读取热度的时候将汉字和空格都加进来了,现在肯定要去掉
        tmp=str(i[2]).split()
        if(len(tmp)==1):
            y.append((int)(i[2]))
        else:
            y.append((int)(tmp[1]))
    # bath绘制水平条形图
    plt.barh(range(1,cnt),y,color='steelblue')
    plt.yticks(range(1,cnt),x)
    plt.xlim(120000,2600000)
    plt.xlabel("标题")
    plt.ylabel("热度")
    plt.title("微博热搜前"+str(cnt-1))
    for m, n in enumerate(y):
        # 前两个参数表示标签的坐标位置,第三个参数表示标签的值
        plt.text(n,m+1,n, va='center')
    plt.show()

        (6)运行结果

        

5、代码

# _*_ coding:utf8 _*_
# -*- codeing = utf-8 -*-
import requests
from bs4 import BeautifulSoup  
import re
import urllib.request, urllib.error  
import xlwt  
import matplotlib.pyplot as plt
findLink = re.compile(r'<a href="(.*?)" target="_blank">')
# 获取标题
findTitle = re.compile(r'<a href="(.*?)" target="_blank">(.*?)</a>')
# 获取热度值
findHot = re.compile(r'<span>(\\w*\\s\\d*)</span>')

def main():
    baseurl = "https://s.weibo.com/top/summary?Refer=top_hot&topnav=1&wvr=6"  
   # baseurl="view-source:file:///C:/Users/%E5%AD%99%E5%BB%BA%E6%9E%97/Desktop/%E5%89%8D%E7%AB%AF/new%201.html"
    datalist = getData(baseurl)
    savepath = "D:\\PyCharm Community Edition 2020.2.3\\微博热搜50.xls"    #当前目录新建XLS,存储进去
    saveData(datalist,savepath)      

def getData(baseurl):
    datalist = [] 
    url=baseurl
    html=askURL(url)
    soup=BeautifulSoup(html,"html.parser")
    for item in soup.find_all('td',class_="td-02"):
        data=[]
        item=str(item)
        if len(re.findall(findLink,item))==0:
                continue
        link=re.findall(findLink,item)[0]
        data.append(link)
        # print(re.findall(findTitle,item))
        title=str(re.findall(findTitle,item)[0])
        title=title.strip('(')
        title=title.strip(')')
        title=title.replace("\\'","")
        title=title.split(",")
        data.append(title[1])
        if len(re.findall(findHot,item))==0:
            data.append("置顶")
            datalist.append(data)
            continue
        hot=re.findall(findHot,item)[0]
        data.append(hot)
        datalist.append(data)
    return datalist



# 得到指定一个URL的网页内容
def askURL(url):
    head = {  # 模拟浏览器头部信息,向服务器发送消息
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73',
         'referer': 'https://s.weibo.com/top/summary?Refer=top_hot&topnav=1&wvr=6',
        'cookie':'SINAGLOBAL=5941149481278.592.1604320169618; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WF9AA32bgUYQHiXEuuVxxJR5JpX5KMhUgL.Fo-4So.ESoMfe0B2dJLoI0qLxK-L1-zLB.BLxKqLBo5L1K2LxK-L1h2LBoqLxK-L1heL12qLxKMLBoBLB-zLxKqL1--LB-zt; ALF=1667371481; SSOLoginState=1635835482; SCF=AkN12N2iMhFTeM7jKDJgpYcYOf5kmH3Ce06tDJU2y2mljYlKd05KkJok_NVeQQlKS5DVQ3MLyNAD0Dq6QEWbaWs.; SUB=_2A25MhK4KDeRhGeNH7VsT9inJyDiIHXVv85jCrDV8PUNbmtANLWLukW9NSp1gXY7kU378-JL7Yek2RvC4p8_BqxCH; _s_tentry=weibo.com; Apache=683258631837.9574.1635836312586; ULV=1635836312593:18:1:1:683258631837.9574.1635836312586:1633605575935; UOR=www.hfut.edu.cn,widget.weibo.com,www.cnblogs.com',

    }
 
    request = urllib.request.Request(url, headers=head)
    html = ""
    try:
        response = urllib.request.urlopen(request)
        html = response.read().decode("utf-8")
    except urllib.error.URLError as e:
        if hasattr(e, "code"):
            print(e.code)
        if hasattr(e, "reason"):
            print(e.reason)
    return html


def saveData(datalist,savepath):
    print("save.......")
    print(datalist)
    book = xlwt.Workbook(encoding="utf-8",style_compression=0) #创建workbook对象
    sheet = book.add_sheet('微博热搜Top50', cell_overwrite_ok=True) #创建工作表
    # col = ["热搜排名","热搜链接","热搜标题","热搜热度"]
    # for i in range(0, 4):
    #     sheet.write(1, i, col[i])  # 列名
    for i in range(1,51):
        print("第%d条" %(i))       #输出语句,用来测试
        data = datalist[i-1]
        print(data)
        if i==1:
            sheet.write(i-1,0,"置顶")
            sheet.write(i-1,1,data[0])
            sheet.write(i-1, 2, data[1])
            sheet.write(i-1, 3, data[2])
        else:
            sheet.write(i-1,0,i-1)
        for j in range(1,4):
            sheet.write(i-1, j, data[j-1])  # 数据
    book.save(savepath) # 保存

if __name__ == "__main__":  # 当程序执行时
    # 调用函数
     main()
     print("爬取完毕!")

       

三、尾言

        好几天没有更新算法题真的是因为事情太多了,期中考试之后一定补回来!完整的python大作业资源链接如下:HFUTpy大作业

        

        

以上是关于合肥工业大学python大作业之爬虫(手把手教你爬取微博热搜)的主要内容,如果未能解决你的问题,请参考以下文章

手把手教你爬取天堂网1920*1080大图片(批量下载)——理论篇

手把手教你爬取天堂网1920*1080大图片(批量下载)——理论篇

手把手教你爬取天堂网1920*1080大图片(批量下载)——理论篇

手把手教你爬取天堂网1920*1080大图片(批量下载)——理论篇

手把手教你爬取天堂网1920*1080大图片(批量下载)——理论篇

两万字零基础爬虫requests初阶教程,手把手教你爬数据(建议收藏)