Python爬虫:爬取网络流行词制作词云

在这里插入图片描述

导入:

最近突然觉得,在生活中可以看到很多与词云类似的图片,(不知道是不是我的个性化广告,哈哈哈)总之,闲来无事,自己也写一个代码来生成词云。
用什么数据来生成呢?再三回忆,突然想起了之前有一个网络流行词的网站: 小鸡词典
在这里插入图片描述
里面不仅有热词,还有对应的解释,以及点赞数,这次的主要目的不是爬虫而是词云,所以我会用点赞数来作为对应热词的值


分析及代码:

首先打开网站首页用开发者工具抓包并构造请求头:
在这里插入图片描述

header = {
    'Host': 'jikipedia.com',
    'Connection': 'keep-alive',
    'Origin': 'https://jikipedia.com',
    'Referer': 'https://jikipedia.com/',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
}

经过查看源码可以看到,初次打开网页时会默认加载 20 条数据。也就是 20 个流行词,仅仅20条数据怎么够,于是我们要先研究一下其余的数据是怎么加载的。
在这里插入图片描述
当我们向下滑动鼠标滚轮时发现,出现了以下的提示:到这里我们就不能再滑动了,但是当我们 登上账号 后就可以看到,我们可以永无止境的向下滑动,而且每次都是加载 20 条数据。
在这里插入图片描述
最开始我还以为当我们向下滑动时,会再次请求一个链接,类似于翻页操作,这就很简单了啊。
虽然当我去检查抓包时,确实发现了如下图的数据,的确是重新请求了一个链接,不过这里又出现了问题:
在这里插入图片描述
我们可以看到,浏览器先发送一个 OPTIONS 请求,得到回复:Message: {“title”:“OPTIONS”,“content”:“收到了OPTIONS请求”} 后再次向同一个网址发送了一个 POST 请求。这才得到了新加载的 20 条数据。
在这里插入图片描述
在这里插入图片描述
到这里,我们是否还需了解一下 OPTIONS 1 请求?
其实啊,完全不用,这里有个特殊情况:细心的你应该会发现,该网站是基于大数据的,会自动给你推送流行词,所以每一次请求主页都会得到不同的 20 条数据
这下我们就只需要写出如何抓取主页的 20 条数据就行了。

注意项:

  • 由于要进行模拟登录,所以还要添加请求头 ‘Cookies’ 字段。
  • 由于要多次请求网页,所以将请求连接写成函数。
  • 由于有些流行词才刚发布,点赞数为0,但是在网页中它是以 ‘ ’(空格)的形式存在的,所以一旦遇到这种情况要将 ‘ ’(空格)改为 0。
def request(url):
    try:
        proxies = {'http': random.choice(proxy)}
        r = requests.get(url=url, headers=header, proxies=proxies)
        r.raise_for_status()
        r.encoding = 'utf-8'
        return r
    except:
        print("请求失败!")
        
def fun(url):
    likes = []
    html = request(num2, url).text
    words = parsel.Selector(html).xpath('//a[@class="card-content"]//strong/text()').extract()
    like_html = parsel.Selector(html).xpath('//div[@class="like button hoverable"]').extract()
    for li in like_html:
        like = parsel.Selector(li).xpath('//div/text()').extract()[-1]
        if like != ' ':  
            likes.append(like)
        else:
            likes.append(0)
    for num in range(20):
        data[words[num]] = int(likes[num])
画词云:

基本功能都已经实现,虽然还是有一点小调整,但是我要留在后面来说为什么。
数据现在已经有了,接下来就是画词云了:
首先导入画词云所需要的模块:

import numpy
import wordcloud
import matplotlib.pyplot as plt

wordcloud 模块只负责生成词云的数据
matplotlibpyplot 类才负责把词云画出来
安装时只需要输入命令: pip install wordcloud ,它会自动将两个模块都安装好。(wordcloud 模块依赖于 matplotlib 模块)

写出关键代码:(详注解)

def draw():
    mask1 = numpy.array(PIL.Image.open('./Desktop/123.jpg')) #词云形状目录
    wcloud = wordcloud.WordCloud(background_color='white', mask=mask1, font_path='‪C:/Windows/Fonts/STXINGKA.TTF') #创建了一个词云(不支持中文,指定字体,不然就是乱码())
    wcloud.generate_from_frequencies(frequencies=data) #以频率(点赞数)为标准生成词云

    plt.figure(dpi=1200)  
    plt.imshow(wcloud, interpolation='bilinear') # 显示内容 (bilinear 点阵方式:二维)
    plt.axis('off')  #关闭坐标显示
    plt.show()  #显示

选用以下图片作为模型,可以画出一个和开篇一样的爱心词云。
在这里插入图片描述

由于该网站的反爬机制,会封掉短时间内频繁访问IP (起码不是人的速度)和当前登录的账号,导致以后即使更换了 IP 登上账号以后还是不能访问该网站。
所以在请求代码的位置尽量多休眠几秒。
如果每次请求都要休眠几秒钟,那我请求 100 次呢?1000 次呢?… 总不可能为了用多一点的数据来画词云,就要在电脑前一直等着吧!
所以我使用了一个告警系统,可以通过微信来提示我的词云生成情况:成功或某一次请求失败!

def warning(num1, flag=1):  # 0  失败    1  成功
    par = {"num": str(num1)}
    if flag == 1:
        temp = '477464252017283072'  //成功时的匹配码
    else:
        temp = '477464742239145984'    //失败时的匹配码
    Parameters = {       //告警参数
        'secretKey': '********************************',
        'appCode': '477462730265071616',
        'templateCode': temp,
        'params': quote(str(par))
    }
    URL = 'https://api.*******.com/api/alarm'
    r = requests.get(URL, params=Parameters)
    print(r.text)

源码及结果:

import PIL
import time
import numpy
import tqdm
import random
import parsel
import requests
import wordcloud
import matplotlib.pyplot as plt
from urllib.parse import quote

header = {
    'Host': 'jikipedia.com',
    'Connection': 'keep-alive',
    'Origin': 'https://jikipedia.com',
    'Referer': 'https://jikipedia.com/',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
    'Cookie': '填写自己的 Cookies '
}
proxy = ['HTTP://106.42.216.132:9999', 'HTTP://115.221.242.206:9999', 'HTTP://163.204.245.77:9999',
         'HTTP://125.110.93.8:9000', 'HTTP://175.43.59.50:9999', 'HTTP://120.83.99.101:9999',
         'HTTP://171.11.178.229:9999', 'HTTP://123.169.168.66:9999', 'HTTP://122.4.43.151:9999',
         'HTTP://122.243.8.64:9000', 'HTTP://1.197.204.239:9999', 'HTTP://163.204.247.121:9999',
         'HTTP://59.62.25.154:9000', 'HTTP://39.96.220.231:8888', 'HTTP://163.204.242.255:9999',
         'HTTP://113.195.17.111:9999', 'HTTP://163.204.246.180:9999', 'HTTP://110.243.31.197:9999',
         'HTTP://114.224.223.164:9999', 'HTTP://125.108.78.245:9000', 'HTTP://120.83.98.217:9999',
         'HTTP://112.84.98.40:9999', 'HTTP://122.4.42.59:9999']

data = {}
num1 = 100


def request(num2, url):
    try:
        proxies = {'http': random.choice(proxy)}
        r = requests.get(url=url, headers=header, proxies=proxies)
        r.raise_for_status()
        r.encoding = 'utf-8'
        return r
    except:
        warning(num2, 0)


def fun(num2, url):
    likes = []
    try:
        time.sleep(3)
        html = request(num2, url).text
        words = parsel.Selector(html).xpath('//a[@class="card-content"]//strong/text()').extract()
        like_html = parsel.Selector(html).xpath('//div[@class="like button hoverable"]').extract()
        for li in like_html:
            like = parsel.Selector(li).xpath('//div/text()').extract()[-1]
            if like != ' ':
                likes.append(like)
            else:
                likes.append(0)
        for num in range(20):
            data[words[num]] = int(likes[num])
    except:
        warning(num2, 0)


def draw():
    mask1 = numpy.array(PIL.Image.open('./Desktop/123.jpg'))
    wcloud = wordcloud.WordCloud(background_color='white', mask=mask1, font_path='‪C:/Windows/Fonts/STXINGKA.TTF')
    wcloud.generate_from_frequencies(frequencies=data)

    plt.figure(dpi=1200)
    plt.imshow(wcloud, interpolation='bilinear')
    plt.axis('off')
    plt.show()


def warning(num1, flag=1):  # 0  失败    1  成功
    par = {"num": str(num1)}
    if flag == 1:
        temp = '477464252017283072'
    else:
        temp = '477464742239145984'
    Parameters = {
        'secretKey': '**************************',
        'appCode': '477462730265071616',
        'templateCode': temp,
        'params': quote(str(par))
    }
    URL = 'https://api.******.com/api/alarm'
    r = requests.get(URL, params=Parameters)
    print(r.text)


if __name__ == '__main__':
    url = 'https://jikipedia.com/'
    for num in tqdm.tqdm(range(num1),desc="正在统计:"):
        fun(num, url)
    warning(num1)
    draw()

结果:

正在统计:: 100%|██████████| 100/100 [07:37<00:00,  4.02s/it]
{"code":"200","data":null,"message":"SUCCESS"}

在这里插入图片描述


  1. OPTIONS 请求方法的主要用途有两个:
    1)获取服务器支持的HTTP请求方法;
    2)用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。 ↩︎

猜你喜欢

转载自blog.csdn.net/qq_44700693/article/details/107151432