前言

笔者近日在做一些数据的收集和处理工作,用到了曾经学到的爬虫知识。当时没有仔细进行整理,决定从本篇开始,介绍使用Python爬虫的相关知识总和。首先介绍最简单的BeautifulSoup来解析HTML上的(也就是网页代码中的信息),涉及到HTML和带参数的XHR技术;而后介绍直接找到后台json数据的爬虫方法,用到的是json解析技术;进而介绍selenium库,这个库将模拟网页行为,获取后台发送的数据,从而简化代码,使其像BeautifulSoup那样进行抓取;最后介绍Scrapy框架及其使用方法。

提示:以下是本篇文章正文内容,下面案例可供参考

一、BeautifulSoup是什么?

BeautifulSoup是一个Python提供的一个第三方HTML网页解析工具包。对于一个网址,可以使用beautifulSoup对象的成员函数,直接找到相应的组件内容。对于像文本、资讯这样的内容,往往都是直接写进网页文本代码的。利用Google浏览器的开发者工具,我们可以直接阅读网页源码:

在这里插入图片描述
可以使用右边那个小箭头直接定位到需要爬取的位置,就可以发现这些资讯都直接写在右边的源码当中。文字类资讯也不需要实时更新,也没有方便的数据维护方式。那么对于开发者而言,我们要做的就是解析这个网页。那么怎么获取到这个网页字符串呢?

二、面向单个网页的使用步骤

1.使用Request库获取到HTML网页

引入requests库,其对象构造函数用法如下:
——requests.get(url,params,headers)
(1)对于单个网页来说,我们只需要url和headers就可以了;
(2)对于连续爬取网页,我们还需要中间的params参数,这和XHR网络技术关联。
下面就以爬取前面那个网页为例,请求其数据:代码如下(示例):

headers = {'user-agent':
             'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) '
             'AppleWebKit/537.36 (KHTML, like Gecko) '
             'Chrome/79.0.3945.79 Safari/537.36'}//Can use yours
url = "https://new.qq.com/omn/20210305/20210305A09CF900.html"
res_news = requests(url=url,headers=headers)
html_news = res_news.text

2.分析HTML网页的构成

首先,对于这样一个简单的新闻,我们只会关心两点:标题和内容(你也可以考虑更多)
标题在h1组件里面:

在这里插入图片描述
内容的话,一般是在div组件里面,class=centent-article
在这里插入图片描述

3.使用bs对象进行find和处理

因此我们可以写出一个简单的代码:

bs = BeautifulSoup(html_news,'html.parser')
title = bs.find('h1').text
content= bs.find('div',class_='content-article').text //注意参数带下划线
content_str = "".join(re.findall('[0-9\u4e00-\u9fa5]', content))

但是我们可以看到,所有的段落都是在p组件里面的,那么代码也可以这样写:代码如下(示例):

cur_str = ""
bs = BeautifulSoup(html_news,'html.parser')
title = bs.find('h1').text
paras = bs.find_all('p',class_='one-p') //返回一个列表
for para in paras:
	cur_str += "".join(re.findall('[0-9\u4e00-\u9fa5]', para.text))

这里使用了正则表达式来提取文字和数字。特别注意的是,find函数找到的是一个元素,而find_all返回的则是一个列表。re.find_all找到的也是一个列表,返回的时候要用””.join来拼接为一个字符串。实际上,BeautifulSoup的底层就是正则表达式,因此在成员函数上也很像。

4.写入文件当中

写入文件就非常简单了,组合一下代码,把数据放进类成员中,采用追加的方式往file中增加文件是不错的选择。代码如下(示例):

with open('file.txt','w+') as f:
	f.write(str)

三、面向连续网页的使用步骤

1.XHR技术加持的带参数网页

不是所有的网页都是由一大串字符组成的url。事实上,在这个超链接的时代,网站架构往往会采用XHR构成的树状结构。例如现在编辑的这个网页:https://editor.csdn.net/md?articleId=114405146就是一个XHR带参数的形式。在问号的前面,是基础网页csdn,在问号的后面就是参数。这里只有一个参数就是articleId。参数的组织是一个字典,这就其实我们可以通过改变参数的值,实现对多页的爬取。代码如下(示例):

for i in range(n):
    print("Now we are crawling the {0}th page".format(i+1))
    res_news = requests.get(self.url,params=self.params,headers=self.headers)
    self.params['pn'] += 10

四、爬取资讯案例

在本案例当中,我们先把百度资讯上搜索的“美军空袭叙利亚”资讯爬取10页,记录两个字段:标题和link,存入到excel表格当中。而后对每一个链接进行爬取。按照标题和所有内容加在一起制作词云(制作词云的代码来自其他网站)。爬取link和爬取内容分别构造了爬虫类。代码如下(示例):

import requests
from bs4 import BeautifulSoup
import re
import openpyxl
import time
import xlrd

from os import path
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import jieba
from wordcloud import WordCloud, STOPWORDS

headers = {'user-agent':
             'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) '
             'AppleWebKit/537.36 (KHTML, like Gecko) '
             'Chrome/79.0.3945.79 Safari/537.36'}

class AttackCrawling_content:
    def __init__(self,headers):
        self.headers = headers
        self.str_all = ""

    def crwal(self,url):
        # get the content
        self.url = url
        res_news = requests.get(self.url,headers=self.headers)
        html_text = res_news.text
        bs = BeautifulSoup(html_text, 'html.parser')
        paras = bs.find_all('p')
        cur_str = ""
        for para in paras:
            content = "".join('% s' % id for id in para)  # 全部转为字符串
            cur_str += "".join(re.findall('[0-9\u4e00-\u9fa5]', content))
        self.str_all += cur_str

class AttackCrawling_link:
    def __init__(self,url,params,headers):
        self.url = url
        self.params = params
        self.headers = headers

    def crawl(self,n):
        # n表示爬取次数
        self.n = n
        self.all_link_title = []
        self.all_links = []
        for i in range(n):
            print("Now we are crawling the {0}th page".format(i+1))
            links_curr_page = []
            titles_curr_page = []
            res_news = requests.get(self.url,params=self.params,headers=self.headers)
            html_text = res_news.text
            bs = BeautifulSoup(html_text,'html.parser')
            a_links = bs.find_all('a',class_='news-title-font_1xS-F')
            for a_link in a_links:
                links_curr_page.append(a_link['href'])
                title = "".join('% s' % id for id in a_link.contents) # 全部转为字符串
                titles_curr_page.append("".join(re.findall('[\u4e00-\u9fa5]',title)))
            self.all_link_title += titles_curr_page
            self.all_links += links_curr_page
            self.params['pn'] += 10

    def writeToFile(self,dirPath,fileName):
        print("Now we are saving information")
        wb = openpyxl.Workbook()
        sheet = wb.active
        sheet.title = time.strftime("%Y-%m-%d", time.localtime())
        sheet.append(['编号', '文章标题', '全文链接'])
        for i,(title,link) in enumerate(zip(self.all_link_title,self.all_links)):
            sheet.append([i+1,title,link])
        wb.save(dirPath + '/' + fileName)
        wb.close()

class WordCloudGenerator:
    def __init__(self, filePath=None, fontPath=None, wc_name=None, maskPath=None):
        d = path.dirname(__file__)  # 当前文件夹
        self.filePath = filePath
        try:
            self.file = open(path.join(d, self.filePath),encoding='gbk').read()
        except BaseException as e:
            print("we use the str in program")
            pass
        self.fontPath = fontPath
        self.maskPath = maskPath
        self.wc_name = wc_name
        try:
            self.mask = np.array(Image.open(path.join(d, self.maskPath)))
        except BaseException as e:
            print("use the defualt mask")
            self.mask = None

    def generateWordCloud(self):
        default_mode = jieba.cut(self.file)
        text = " ".join(default_mode)
        stopwords = set(STOPWORDS)
        stopwords.add("said")
        self.wc = WordCloud(
            # 设置字体,不指定就会出现乱码,这个字体文件需要下载
            font_path=self.fontPath,
            background_color="white",
            max_words=2000,
            mask=self.mask,
            stopwords=stopwords)
        self.wc.generate(text)

    def writeToImg(self):
        d = path.dirname(__file__)
        self.wc.to_file(path.join(d, self.wc_name))

    def show(self):
        plt.imshow(self.wc, interpolation='bilinear')
        plt.axis("off")
        plt.figure()

if __name__ =="__main__":
    needCraw = False
    filePath = r'D:\data\crawling_data\attack_news\新闻列表.xlsx'
    url = 'https://www.baidu.com/s'
    params = {'ie': 'utf-8', 'medium': 0, 'rtt': 1, 'bsst': 1, 'rsv_dl': 'news_t_sk', 'cl': 2, 'wd': '美军空袭叙利亚',
              'tn': 'news', 'rsv_bp': 1, 'tfflag': 0, 'x_bfe_rqs': '03E80000001', 'x_bfe_tjscore': '0.100000',
              'tngroupname': 'organic_news', 'newVideo': 12, 'pn': 0}
    headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) '
                             'AppleWebKit/537.36 (KHTML, like Gecko) '
                             'Chrome/79.0.3945.79 Safari/537.36'}
    try:
        data = []
        wb = openpyxl.load_workbook(filePath)
        table = wb.get_sheet_by_name("2021-03-02")
        n_rows = table.max_row
        for row in range(2,n_rows):
            data.append({"title":table.cell(row,2).value,"link":table.cell(row,3).value})

        # 按照标题产生词云
        link_title= []
        str_title = ""
        for item in data:
            link_title.append(item['link'])
        str_title = " ".join(link_title)
        //wcg_title = WordCloudGenerator(fontPath='msyh.ttc', wc_name="titleWordCloud.tif")
        //wcg_title.file = str_title
        //wcg_title.generateWordCloud()
        //wcg_title.writeToImg()
        //wcg_title.show()

        #按照内容产生词云
        attCrawlCont = AttackCrawling_content(headers=headers)
        for i,link in enumerate(link_title):
            print("Now we are crawling content in {0}th page".format(i+1))
            attCrawlCont.crwal(url=link)
            with open('all_files.txt','w+') as f:
                f.write(attCrawlCont.str_all)

        wcg_content = WordCloudGenerator(filePath='all_files.txt',fontPath='msyh.ttc',wc_name="contentWordCloud.tif")
        wcg_content.generateWordCloud()
        wcg_content.writeToImg()
        wcg_content.show()

    except BaseException as e:
        print("The file does not exist, so we begin crawling")
        attackCraw = AttackCrawling_link(url=url, params=params, headers=headers)
        dirPath = r'D:\data\crawling_data\attack_news'
        attackCraw.crawl(13)
        attackCraw.writeToFile(dirPath=dirPath,fileName="新闻列表.xlsx")

共计124篇资讯:
在这里插入图片描述

制作的内容词云如下:

在这里插入图片描述

总结

以上就是使用BeautifulSoup进行简单资讯文本爬取的处理方法,时间关系,文章中很多编码实现细节没有展开,本帖在笔者遇到的新的问题时会持续更新。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐