手把手系列:用Python3+PyQt5做一个有界面的小爬虫(三)


升级装备,让你的小爬虫变得更能干

在上一篇文章中我们讲到了如何用requests对网页进行GET和POST请求,以及用BeautifulSoup对请求到的页面进行分析。但是单单这样,小爬虫只能对单一的页面进行爬取,还并不能完全解放我们的人力,只能算是个半自动的爬虫,那么这篇文章中,我们要给这只小爬虫再升级几件厉害的装备,让它变得更能干!

任务目标

首先,我们面前有一份长长的Excel表格,里面提供了文章的案例编号,需要爬虫根据这份表格里面的案例编号,来进行相对应文章的爬取。其次,我们还需要对每篇文章爬取的内容做保存,不然关掉IDE后,数据就全部丢失了。

根据这份需求,我们需要给这只小爬虫装备上2件装备

  • 对Excel文件的操作
  • 对文件进行读取和写入操作
装备一:对Excel文件的读取操作

第一步还是需要邀请pip来配合,通过pip来自动安装xlrd模块。

pip install xlrd

这儿解释下,xlrd模块是对Excel进行读取操作的,写入操作需要另外一个叫xlwt的模块来支持。这儿我们只需要读取操作,所以只要引用xlrd模块就行了。

这份是我们需要导入读取的Excel文件,我们要读取里面第二列的全部案例编号,并取出来。

import xlrd #导入excel 模块

# 打开EXCEL文档获取内容
def read_excel():
   #文件位置
    ExcelFile=xlrd.open_workbook(r'C:\Users\Saber\Desktop\案例编号清单.xlsx')
    #获取目标EXCEL文件sheet名
    sheet = ExcelFile.sheet_by_name('Sheet1')
    print('该Excel文档共有' + str(sheet.nrows-1) + '篇文章需要爬取\n')
    #获取第二列的值
    rows = sheet.col_values(1)
    #删除第一行的标题
    rows.pop(0)
     
    # 清洗数据,把空格的都排除掉
    new_excel = []
    for a in rows:
        a = a.replace(u'\xa0', u'').replace(u' ', u'')
        new_excel.append(a)
    print('获取案例编号成功\n')
    return new_excel

因为Excel里会有空格,所以获得原始数据后要再把空格都排除掉。\xa0 是不间断空白符。执行一下,成功获取到我们希望得到的数据。

装备二:对文件进行读取和写入操作

完成了Excel文件的读取,我们还要将数据进行输出。这个需要引用一个OS模块,这个OS模块包含在Python的标准库中,不需要额外的安装。这儿我们还是拿刚才的导入Excel表数据做例子,将Excel表中的信息输出到文件中并保存起来。

def read_excel():
    ## ......上面代码省略......##
    print('获取案例编号成功\n')

    # 以写的方式打开ExcelData.txt
    pageFile = open('ExcelData.txt','a+')
    # 写入内容
    pageFile.write('该EXCEL文档共有' + str(sheet.nrows-1) + '篇文章需要爬取\n')
    # 打开了记得还要关闭
    pageFile.close()

    return new_excel

read_excel()末尾加入这么三行代码。即可以完成文件的打开,内容写入,文件关闭的操作,非常简单。效果如下图所示。

这里要讲解一下open()这个方法。这个方法里面需要传入2个参数open("文件名","打开方式"),其中打开方式中有很多种。我在这儿选择了a+的方式,因为他会判断文件是否存在,不存在则新建,存在则追加数据,非常适合这次的使用场景。

r 打开只读文件,该文件必须存在。
r+ 具有读写属性,从文件头开始写,保留原文件中没有被覆盖的内容,文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 具有读写属性,写的时候如果文件存在,会被清空,从头开始写,如文件不存在则创建该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。

w+与r+区别:r+:可读可写,若文件不存在,报错;w+: 可读可写,若文件不存在,创建
r+与a+区别:r+,可读写,文件必须文件,从文件开头开始写;a+,可读写,文件存在则在文件末尾追加写;文件不存在则创建文件

更详细的讲解可以参考这篇文章:python文件操作

到此为止,小爬虫的两个新装备都介绍完毕啦,我们要对爬虫改造下,让他能一键自动按照指定Excel表中的案例编号进行自动爬取,并输出保存爬取到的内存。

改造小爬虫

在上面我们获取了全部需要爬取的案例编号后,进入网站发现,每篇文章的caseId编号并不是使用这个案例编号的,需要通过案例编号搜索,来获取每篇文章的caseId,然后才能根据这个caseId来对文章和文章的评论进行访问。那么,我们要先通过这个案例编号来获取文章的caseId。

输入案例编号,点击搜索后会出现相对应的文章。

然后对文章标题进行审查元素,可以看到这是个超链接,文章caseId就在这个超链接中http://xxx.action?caseId=287270。但是经过摸索,发现前面的那个input标签的value也带有这个caseId,而且这个input标签全文只有2个,比起a标签来更方便解析,于是我决定取这个input标签中value值。

通过抓取请求,分析,这是一个POST请求。Form参数一共有6个,需要填写的有3个,其中核心参数就是这个caseSn。

# 爬取文章链接
class BeautifulGetUrl():
    def __init__(self,sn,header):  #类的初始化操作
        self.article_caseId = []  #爬取到的caseId都放到这个list中,实例化这个类后可以随时调用
        self.headers = header  #给请求指定一个请求头来模拟chrome浏览器
        self.web_url = 'http://net.chinamobile.com/cmccnkms-case/web/casemajor/caseList.action'  #要访问的网页地址
        self.caseSn = sn
    def get_caseId(self):
        print('开始文章CaseID获取的POST请求')
        for x in self.caseSn:
            web_data = {
                'pageNum':'',
                'majorName':'无线',
                'orderType':'ZRequestTime',
                'caseTitle':'',
                'caseSn':x,
                'caseAuthor':''
            }
            r = requests.post(self.web_url,data=web_data, headers=self.headers)
            find_tag = BeautifulSoup(r.text, 'lxml').find_all('input',type='checkbox')
            #判断是否可以搜索到文章,搜不到就把内容置为-1
            if len(find_tag) == 1:  
                self.article_caseId.append('-1') 
                print(x)
                print('找不到该篇文章')
            else:
                self.article_caseId.append(find_tag[1]['value']) #获取标签的value值
                print(x)
                print(find_tag[1]['value'])

来解读一下这段代码,这儿因为这几个请求的header部分都是一样的,所以精简代码,把header作为参数来传入,同时也传入之前获取到的案例编号列表。

通过for循环,将caseSn一个一个的传入,作为form的参数,然后对地址进行post请求。在BeautifulSoup中对标签为input,类型为checkbox进行解析,如果能搜索到这篇文章,就会有两个tag,取第二tag的value值就是我们所需要的caseId,然后将这个追加到之前定义的article_caseId中。

这儿增加一个判断是因为,如果该篇文章正在审核或者审核没有通过的话,是搜索不到的。搜索不到的话,解析到checkbox的tag只有一个,我们默认取第二个tag程序就会报错,这个是需要避免的,所以增加了一个判断。如果解析出来的tag只有一个,那么就说明找不到该篇文章,加入一个-1的标识符,如果不加的话,那么article_caseId的下标就和之前Excel获取到的案例编号下标对不上号了,必须要加上去,不能略过。

再来改造下最后类的实例化操作
附上完整的代码块

import xlrd #导入excel 模块
import os  #导入os模块
import requests #导入requests 模块
from bs4 import BeautifulSoup  #导入BeautifulSoup 模块

# 打开EXCEL文档获取内容
def read_excel():
    ......

# 爬取文章caseId
class BeautifulGetCaseId():
    def __init__(self,sn,header):  #类的初始化操作
        ......
    def get_caseId(self):
        ......

# 爬取文章案例编号和当前周期有效阅读数
class BeautifulGetCaseSNandReadNum():
    def __init__(self,i,a,header):  #类的初始化操作
        ......
    def get_data(self):
        ......

# 爬取具体信息
class BeautifulGetData():
    def __init__(self,i,caseId,caseSN,header):  #类的初始化操作
        ......
    def get_data(self):
        ......

#给请求指定一个请求头来模拟chrome浏览器
header = {
    ...... 
}

new_excel = read_excel() #从excel中获取需要的信息
print('获取案例编号成功')

beauty = BeautifulGetCaseId(new_excel,header)  #创建类的实例,根据Excel爬取文章caseId
beauty.get_caseId  #执行类中的方法
print('爬取文章链接成功')

for i,caseId in enumerate(beauty.article_caseId):
    if caseId == '-1':
        pageFile = open('ExcelData.txt','a+')  #以写的方式打开ExcelData.txt
        pageFile.write('这是爬取的第' + str(i+1) + '篇文章\n案例编号:' + new_excel[i] + '\n\n该案例编号搜索不到相对应的文章\n')
        pageFile.close()#开了记得关
        continue
    getCaseSN = BeautifulGetCaseSNandReadNum(i,caseId,header)  #创建类的实例,根据caseId爬取文章基础信息
    getData = BeautifulGetData(i,caseId,new_excel[i],header)  #创建类的实例,根据caseId爬取文章评论信息
    getCaseSN.get_data()
    getData.get_data()

print('爬取具体信息成功')

非常完美,爬取成功,忍不住都想夸一下能干的自己。

后记

截止到现在,一个能解放人力的小爬虫诞生了。但是现在还有个问题,这个爬虫并不是自己使用,而是要交给需要的人来操作使用的,交付的人可不会这些代码(但是他们如果看了我这个手把手系列,我相信他们也一定能熟练的调教这只小爬虫),虚要改一些参数的话,就会变得很麻烦。那么我们就需要给这只小爬虫做一套衣服,让他成为一只界面友好型的小爬虫,然后安安心心的交给他人来使用。在下篇文章中我就来讲一讲Python的图形界面。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容