城市生活知识图谱 ①百度贴吧爬虫源码,长沙吧,长沙美食吧

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文发布时间为2022年1月7日,超过一定日期,代码可能无法运行。
本文为构建城市生活知识图谱的第一步,即使用爬虫对百度贴吧中,长沙吧,长沙美食吧进行数据爬取。
此外,百度贴吧各个吧的网页结构是一致的,所以只需改一下url就可以使得该代码爬取任意其他贴吧!

一、数据格式

毫无疑问,语言选python,数据结构选json,json格式其实就是python中的字典格式,相当于将一个字典结构写入了txt文件,读出也只需要一行代码。
由于爬虫存在很多种异常中断的可能,我们使用两个json文件,其中:
data.json 用于存储已经爬取的内容
如果程序中断,则需要重新读取data.json中已经记录的内容,通过内容长度跳到指定的页面,使用hash值跳过已经爬取过的内容。
newdata.json 用于记录每次程序新爬取的内容

json文件的格式并不是单一的,这里我使用的格式不是常用的格式,因为需要对url计算hash值来达到去重的目的,显然如果给以爬取的内容用list封装是没法达到O(1)的。虽然也可以把list转成dict,但这里data.json里面是一个标准的dict格式,使用read直接可以读成一个dict数据,这也使得每次爬取失败之后都需要手动将newdata.json的数据复制到data.json里面来,其实是偷懒没写这部分。
json文件的格式如下:
{‘herf’:{‘title’:title,‘content’:content,‘time’:time,‘username’:username,‘usertitle’:usertitle,‘userlv’:userlv}}

key含义
herf唯一标识符,url
title帖子标题
content帖子楼主内容
time帖子时间
username用户名
usertitle用户头衔
userlv用户等级

二、网站分析

https://tieba.baidu.com/f?kw=%E9%95%BF%E6%B2%99%E7%BE%8E%E9%A3%9F&ie=utf-8&pn=0

贴吧使用pn指代页数,每页有50个帖子,第一页pn=0,第二页pn=50,第三页pn=100,以此类推。

网页的话,打开对应的网址,按F12,然后按照下图找到自己想要爬取的标签:
在这里插入图片描述

其他的就是使用beautifulsoup去找对应的标签名,如下图,要找time的话,先找到time所在的标签对,在find参数里面填入对应的参数就好了,此外还有find_all方法用于找到所有类似的特征。
在这里插入图片描述

三、源码

# -*- coding: UTF-8 -*-
"""
@狮子搏兔:2022/1/7
"""
import random
import time
import asyncio
import logging
import tkinter
import pandas as pd
from pyppeteer import launcher
import json
from bs4 import BeautifulSoup
from lxml import etree
launcher.DEFAULT_ARGS.remove("--enable-automation")  # 必须在 from pyppeteer import launch 去除浏览器自动化参数
from pyppeteer import launch


def get_existdata(path):
    with open(path, "r", encoding='utf-8') as f:
        data = json.loads(f.read())
    f.close()
    return data


def getherf(html):
    bsobj = BeautifulSoup(html, 'html.parser')
    cnt = bsobj.find_all('a', attrs={'class': "j_th_tit"})
    dic = {}
    if cnt:
        for item in cnt:
            if item.text and item["href"] and '本吧吧主火热招募中' not in item.text:
                dic[item["href"]] = item.text
    return dic


def getcontent(herf, title, html):
    usertitle = ''
    userlv = ''
    usergrade = ''
    username = ''
    content = ''

    bsobj = BeautifulSoup(html, 'html.parser')
    time = bsobj.find('div', attrs={'class': "post-tail-wrap"})
    if time:
        time = time.find_all('span', attrs={'class': "tail-info"})[-1]
        if time:
            time = time.text
    content = bsobj.find('div', attrs={'class': "d_post_content j_d_post_content"})
    if content:
        content = content.text
    username = bsobj.find('a', attrs={'class': "p_author_name j_user_card"})
    if username:
        username = username.text
    usergrade = bsobj.find('a', attrs={'class': "user_badge d_badge_bright d_badge_icon2"})
    if usergrade:
        usertitle = usergrade.find('div', attrs={'class': "d_badge_title"}).text
        userlv = usergrade.find('div', attrs={'class': "d_badge_lv"}).text
    return {herf: {'title':title,'content':content,'time':time,'username':username,'usertitle':usertitle,'userlv':userlv}}


async def main():
    # 浏览器 启动参数
    start_parm = {
        # 启动chrome的路径
        "executablePath": r"C:\Program Files\Google\Chrome\Application\chrome.exe",
        # 关闭无头浏览器
        "headless": False,
        "args": [
            '--disable-infobars',  # 关闭自动化提示框
            # '--window-size=1920,1080',  # 窗口大小
            '--log-level=30',  # 日志保存等级, 建议设置越好越好,要不然生成的日志占用的空间会很大 30为warning级别
            # '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36',
            # UA
            '--no-sandbox',  # 关闭沙盒模式
            '--start-maximized',  # 窗口最大化模式
            # '--proxy-server=http://localhost:1080'  # 代理
            # r'userDataDir=D:\project_demo\python_demo\spider_demo\JavaScript 逆向系列课\userdata'  # 用户文件地址
        ],
    }

    browser = await launch(**start_parm)
    page = await browser.newPage()
    contentpage = await browser.newPage()
    tk = tkinter.Tk()
    width = tk.winfo_screenwidth()
    height = tk.winfo_screenheight()
    tk.quit()

    await page.setViewport(viewport={'width': width, 'height': height})

    # 第二步,修改 navigator.webdriver检测
    # 其实各种网站的检测js是不一样的,这是比较通用的。有的网站会检测运行的电脑运行系统,cpu核心数量,鼠标运行轨迹等等。
    # 反爬js
    js_text = """
() =>{ 
    Object.defineProperties(navigator,{ webdriver:{ get: () => false } });
    window.navigator.chrome = { runtime: {},  };
    Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
    Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], });
 }
    """
    await page.evaluateOnNewDocument(js_text)  # 本页刷新后值不变,自动执行js
    headurl = 'https://tieba.baidu.com/' # 内容url头部,用于拼接herf
    url = 'https://tieba.baidu.com/f?kw=%E9%95%BF%E6%B2%99%E7%BE%8E%E9%A3%9F&ie=utf-8&pn=' # 页数url头部
    existdata = get_existdata('data.json') # 读取目前有多少数据
    num = len(existdata) // 50 * 50  # 计算应该跳到的页数

    with open("newdata.json", "w", encoding='utf-8') as f:
        while num <= 1900: # 最大为39页,每页50条数据
            print('正在爬取第{}页'.format(num))
            await page.goto(url + str(num)) # 访问herf页面
            herftext = await page.content() # 获取html
            herfdic = getherf(herftext) # 获取所有herf
            time.sleep(random.randint(30, 60))  # 随机等待,单位秒
            for herf in herfdic:
                if herf not in existdata: # 去重
                    herfurl = headurl + herf # 拼接herf url
                    await contentpage.goto(herfurl)
                    contenttext = await contentpage.content() # 获取内容
                    contentdic = getcontent(herf, herfdic[herf], contenttext)
                    f.write(json.dumps(contentdic, indent=0))
                    time.sleep(random.randint(2, 10))
            num += 50
    f.close()
    # await browser.close()

if "__main__" == __name__:
    asyncio.get_event_loop().run_until_complete(main())

总结

这种不需要登录的静态网页,爬取还是挺容易的,就是没有分布式,也有点担心爬太快了会封ip,所以等待时间还是挺长的。
json文件可视化结果,可见长沙美食吧基本上都是一些培训广告,焯。
下面是爬取到的json文件。
长沙美食吧所有帖子.json
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是狮子搏兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值