三小时爬取四万份年报

三小时爬取四万份年报

本文爬虫的任务提交速度以及下载速度:
在这里插入图片描述

在这里插入图片描述

年报文本分析

如果你有年报文本分析需求,首先你就要获取上市公司年报
关于上市公司年报的爬虫已经有很多篇了,如下:

《30行代码轻松爬取全部A股公司年报》
《实战演练-爬取深交所年报》
《【爬虫】用Python爬取公司年报》
《Python爬取上交所年报并下载》
后三个太麻烦了,需要分析网页,爬取链接,第一个最简单,因为他直接给出了所有公司年报的下载链接(好人一生平安),秉承着能复制粘贴绝不自己写的原则,选了第一个。但是这几个爬虫都有两个问题:
1.如果你爬着爬着中断了,需要重新爬取
2.都是单线程,尤其是这种网络IO,效率低

因此,本文对第一个爬虫使用了多线程进行了改写,还使用了xlwings记录实时下载状态。

爬虫思路

先看一下第一个爬虫给出的excel文件:
在这里插入图片描述
其实我们只需要前六列,因此我们将其复制粘贴到Excel中,再加入第七列标题state,以后用来记录下载状态。
注意:电脑上必须要有微软的office,否则后面爬虫没法用
在这里插入图片描述
简单来讲,我们的爬虫分为这几部分:
1.读取excel数据,获取下载链接
2.发起请求,接收pdf数据
3.把pdf进行存储
我们可以简化一下:
1.读取excel数据,获取下载链接
2.发起请求,接收pdf数据,把pdf进行存储
所以单线程爬虫总共分两步,但是每个文件下载都要等十几秒,效率非常低。

本文使用多线程的思路,多线程就是一心多用,如果你需要扫地,还需要蒸米饭,那你可以先把电饭煲打开,然后去扫地。当你做的每件事情都要等待,一心多用就会大大提高效率。

本文的思路是,把每个下载当成一个任务,先找个“任务管理员”,我们叫他Queue(),把所有下载任务全都告诉他,然后我们一心八用(开八门开八个线程),找Queue()要任务,执行任务,再去要任务,直到Queue()的任务全都派出去执行完了。代码如下:

先把需要的库导入,不需要问为什么

import xlwings as xw
import requests
from queue import Queue
import threading
import os
import time

进行一些初始化操作

    # 把我们的excel路径写下来
    file_path = r'E:\python\firmreport_spider2.0\公司竞争战略指标_2001_2019.xlsx'
    # 初始化,打开excel,统计行列数
    wb = xw.Book(file_path)
    sht = wb.sheets['公司竞争战略指标_2001_2019']
    #等待一会,打开太慢了
    time.sleep(6)
    # 获取行列数
    info = sht.used_range
    rows = info.last_cell.row
    columns = info.last_cell.column
    #创建一个任务管理员Queue()
    q = Queue()
    #设置八个线程
    num_threads = 8

读取数据,提交给任务管理员q

# 把所有未下载链接提交给任务管理员q,等线程接取任务
def put_queue():
	#把excel的所有数据给lists
    lists = sht.range('A1').expand('table').value
    #从第二行开始,逐行读取,直到最后一行
    for i in range(1, rows):
    	#如果第i行的第7列(state那列)是空的,也就是没下载,把那行的数据提交给q
        if not lists[i][6]:
            code = str(int(lists[i][0]))
            firm = lists[i][1].replace("*", "")
            url = lists[i][4]
            year = lists[i][2].year
            n = i
            q.put([code, firm, year, url, n])
            print(code, firm, year, url, n)

定义存储路径和文件名称

# 这个函数只要拿到code,firm,year就能返回存储路径+文件名
def get_filepath(code, firm, year):
    file_path = 'E:\\python\\firmreport_spider2.0\\下载年报'
    file_name = "{}-{}-{}年年度报告.pdf".format(code, firm, year)
    file_full_name = os.path.join(file_path, file_name)
    return file_full_name

发送请求,进行下载,然后存储

# 拿到下载链接和存储路径,就会自动下载并存储
def download_pdf(url, file_full_name):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60'}
    res = requests.get(url, headers=headers)
    with open(file_full_name, "wb") as fp:
        for chunk in res.iter_content(chunk_size=1024):
            if chunk:
                fp.write(chunk)

定义一个线程需要做的事,取任务,设置下载路径并制定文件名,下载存储,队列任务减一

#利用前面的函数定义一个线程需要做的事,取任务,设置下载路径并制定文件名,下载存储,队列任务减一
def single():
    while True:
    	#找q要任务信息
        info = q.get()
        #根据任务信息先把文件全名和路径写好
        file_full_name = get_filepath(info[0], info[1], info[2])
        #把链接和路径给下载器,自动下载和存储
        download_pdf(info[3], file_full_name)
        #下载成功后在excel中记录为YES
        xw.Range((info[4]+1, 7)).value = 'YES'
        #q里的任务数减一
        q.task_done()

最后写一个总的流程

if __name__ == '__main__':
    # 把我们的excel路径写下来
    file_path = r'E:\python\firmreport_spider2.0\公司竞争战略指标_2001_2019.xlsx'
    # 初始化,打开excel,统计行列数
    wb = xw.Book(file_path)
    sht = wb.sheets['公司竞争战略指标_2001_2019']
    #等待一会,打开太慢了
    time.sleep(6)
    # 获取行列数
    info = sht.used_range
    rows = info.last_cell.row
    columns = info.last_cell.column
    #创建一个任务管理员Queue()
    q = Queue()
    #设置八个线程
    num_threads = 8

	#开启八门
    for i in range(num_threads):
        t = threading.Thread(target=single)
        t.daemon = True
        t.start()

    # 启动任务管理员
    put_queue()
    #等待q里的任务没了结束
    q.join()

所有的代码如下:

import xlwings as xw
import requests
from queue import Queue
import threading
import os
import time



# 把所有未下载链接放入队列,放入队列就变成了任务,等线程接取任务
def put_queue():
    lists = sht.range('A1').expand('table').value
    for i in range(1, rows):
        if not lists[i][6]:
            code = str(int(lists[i][0]))
            firm = lists[i][1].replace("*", "")
            url = lists[i][4]
            year = lists[i][2].year
            n = i
            q.put([code, firm, year, url, n])
            print(code, firm, year, url, n)


# 定义存储路径和文件名称
def get_filepath(code, firm, year):
    file_path = 'E:\\python\\firmreport_spider2.0\\下载年报'
    file_name = "{}-{}-{}年年度报告.pdf".format(code, firm, year)
    file_full_name = os.path.join(file_path, file_name)
    return file_full_name


# 发送请求,进行下载,然后存储
def download_pdf(url, file_full_name):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60'}
    res = requests.get(url, headers=headers)
    with open(file_full_name, "wb") as fp:
        for chunk in res.iter_content(chunk_size=1024):
            if chunk:
                fp.write(chunk)

#定义一个线程需要做的事,取任务,设置下载路径并制定文件名,下载存储,队列任务减一
def single():
    while True:
        info = q.get()
        file_full_name = get_filepath(info[0], info[1], info[2])
        download_pdf(info[3], file_full_name)
        #下载成功后记录为YES
        xw.Range((info[4]+1, 7)).value = 'YES'
        q.task_done()


if __name__ == '__main__':
    # 初始化,打开excel,统计行列数
    file_path = r'E:\python\firmreport_spider2.0\公司竞争战略指标_2001_2019.xlsx'
    wb = xw.Book(file_path)
    sht = wb.sheets['公司竞争战略指标_2001_2019']
    time.sleep(3)
    # 获取行列数
    info = sht.used_range
    rows = info.last_cell.row
    columns = info.last_cell.column
    # print(rows,columns)
    q = Queue()
    num_threads = 8

    for i in range(num_threads):
        t = threading.Thread(target=single)
        t.daemon = True
        t.start()

    # 启动队列
    put_queue()
    q.join()

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值