首页 > 编程语言 >【Python】爬虫之多线程

【Python】爬虫之多线程

时间:2024-05-02 20:56:54浏览次数:16  
标签:xpath name Python price tr 爬虫 num year 多线程

线程

先来理解一下线程的作用,假如有一个工厂,这个工厂里面只有一条生产线,这一条生产线每周可以生产10件产品,像这样的情况就可以理解为单线程。那么问题来了,如果这家工厂收到了一个生产委托,需要在一周之内生产20件产品,这个时候工厂就可以增加一条生产线,提升产能,这个情况就可以理解为多线程

线程在爬虫中的使用

我们简单理解了一下线程,那么我们思考一下,正常写一个爬虫的思路是什么?例如下图展示的电影票房,需要爬取这些电影票房的过程是什么?

首先我们需要有目标网页的URL地址,通过requests这个Python库去请求这个地址,获取网页源代码,然后通过xpath、bs4、re等方法在网页源代码中解析出我们想要的内容。最后把这些内容写入到文件中。这样我们就可以获取一年的电影票房数据。

import requests
from lxml import etree
# 处理解析后的内容
def str_tools(lst):
    if lst:
        s = ''.join(lst)
        return s.strip()
    else:
        return ''

f = open('1996.csv', 'w', encoding='utf-8')
# 抓取1996年电影票房 注意这个url无法访问
url = 'http://www.url.com/boxoffice1996'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'
}

resp = requests.get(url, headers=headers)
tree = etree.HTML(resp.text)
trs = tree.xpath('//table/tbody/tr')[1:]
for tr in trs:
    num = tr.xpath('./td[1]//text()')
    year = tr.xpath('./td[2]//text()')
    name = tr.xpath('./td[3]//text()')
    price = tr.xpath('./td[4]//text()')
    # print(num, year, name, price)
    num = str_tools(num)
    year = str_tools(year)
    name = str_tools(name)
    price = str_tools(price)
    # print(num, year, name, price)
    f.write(f'{num},{year},{name},{price}\n')

现在问题来了假如我想获取很多年的电影票房数据,从1996年至今的所有电影票房数据,怎么写呢?每一年的数据对应的URL是不同的,首先需要研究一下URL的规律,这里不难看出'http://www.url.com/boxoffice1996'这个地址的改变规律是最后的面的年份,假如是2024年的数据地址就是http://www.url.com/boxoffice2024。所以我们可以把代码封装成一个函数。把这个年份动态的传入进来。

import os.path

import requests
from lxml import etree
import time


def str_tools(lst):
    if lst:
        s = ''.join(lst)
        return s.strip()
    else:
        return ''

def get_one_year(year):
    path = '电影票房'
    f = open(os.path.join(path, f'{year}.csv'), 'w', encoding='utf-8')
    # 抓取year年电影票房
    url = f'http://url/boxoffice{year}'
    # 请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'
    }
    # 发送请求
    resp = requests.get(url, headers=headers)
    # 解析页面
    tree = etree.HTML(resp.text)
    # 解析数据
    trs = tree.xpath('//table/tbody/tr')[1:]
    for tr in trs:
        num = tr.xpath('./td[1]//text()')
        year = tr.xpath('./td[2]//text()')
        name = tr.xpath('./td[3]//text()')
        price = tr.xpath('./td[4]//text()')
        # print(num, year, name, price)
        num = str_tools(num)
        year = str_tools(year)
        name = str_tools(name)
        price = str_tools(price)
        # print(num, year, name, price)
        f.write(f'{num},{year},{name},{price}\n')

if __name__ == '__main__':
    t1 = time.time()
    for year in range(1996, 2025):
        get_one_year(year)
    print(time.time() - t1)
    # 耗时 45 秒

这段程序在我的电脑上运行需要45秒才能获取到1996年到2024年所有的电影票房数据,这时候问题来了,如果我们想快一点来获取数据,怎么办呢?我们就可以考虑把程序改为多线程。我们需要了解一个东西叫线程池,我们可以把它理解为工厂,我们向页面发送请求的函数可以理解为生产线,我们多组键几个生产线,把它放到工厂里,效率不就上来了。

那么在python中如何使用线程池呢?首先需要导入from concurrent.futures import ThreadPoolExecutor使用代码如下

from concurrent.futures import ThreadPoolExecutor
import time

def task(name):
    print(f"Task {name} starting")
    time.sleep(2)
    print(f"Task {name} finishing")

# 创建一个ThreadPoolExecutor对象,指定线程数量为3
with ThreadPoolExecutor(max_workers=3) as executor:
    # 提交任务给线程池
    executor.submit(task, "A")
    executor.submit(task, "B")
    executor.submit(task, "C")

看到这个with ThreadPoolExecutor(max_workers=3) as executor:代码我觉得大家应该不会陌生,是不是和文件处理with open() as f:。文件处理中这样写是因为它可以帮助我们正确的关闭文件,它们是同样的道理,executor就可以理解为别名。max_workers=3这个参数是指定线程数为3,可以更改线程数,但是建议小一点,这是对网站的保护。executor.submit(task, "A")提交任务到线程池,任务就是我们自己写的函数,其中这个A是实参传递给函数task的形参name。到这我们大致了解了如何开启多线程。

我们来理一下思路:

  1. 我们封装了一个函数get_one_year,他的作用是向目标网页发送请求,解析出我们需要的数据。
  2. 我们需要的不是一年的数据,而是很多年的,所以我们需要动态的调整URL,所以我们需要向函数传递一个参数。
  3. 需要创建一个线程池,将我们的函数get_one_year作为任务丢进去

完整代码如下:

import os.path

import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor
import time


def str_tools(lst):
    if lst:
        s = ''.join(lst)
        return s.strip()
    else:
        return ''

def get_one_year(year):
    path = '电影票房'
    f = open(os.path.join(path, f'{year}.csv'), 'w', encoding='utf-8')
    # 抓取year年电影票房
    url = f'http://url/boxoffice{year}'
    # 请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'
    }
    # 发送请求
    resp = requests.get(url, headers=headers)
    # 解析页面
    tree = etree.HTML(resp.text)
    # 解析数据
    trs = tree.xpath('//table/tbody/tr')[1:]
    for tr in trs:
        num = tr.xpath('./td[1]//text()')
        year = tr.xpath('./td[2]//text()')
        name = tr.xpath('./td[3]//text()')
        price = tr.xpath('./td[4]//text()')
        # print(num, year, name, price)
        num = str_tools(num)
        year = str_tools(year)
        name = str_tools(name)
        price = str_tools(price)
        # print(num, year, name, price)
        f.write(f'{num},{year},{name},{price}\n')


if __name__ == '__main__':
    t1 = time.time()
    with ThreadPoolExecutor(16) as t:
        for y in range(1996, 2025):
            t.submit(get_one_year, y)
    print(time.time() - t1)
    # 多线程耗时 23 秒

标签:xpath,name,Python,price,tr,爬虫,num,year,多线程
From: https://www.cnblogs.com/wangchijiao/p/18170529

相关文章

  • 爬虫概述
    一、什么是爬虫爬虫(Crawler)是一种按照既定规则,在网络上自动爬取信息的程序或脚本。也称为网际网路蜘蛛(InternetSpider)或网络机器人(WebRobot)。爬虫可以自动抓取网络信息,主要用于网站数据采集、内容监测等。二、爬虫能做什么1、搜索引擎搜索引擎利用爬虫发现网络上的......
  • 【Python&文字识别】基于HyperLPR3实现车牌检测和识别(Python版本快速部署)
        闲来无事,想复现一下网上的基于YOLOv5的单目测距算法。然后就突然想在这个场景下搞一下车牌识别,于是就有了这篇文章。今天就给大家分享基于HyperLPR3实现车牌检测和识别。原创作者:RS迷途小书童博客地址:https://blog.csdn.net/m0_56729804?type=blog1、HyperLP......
  • 用 Python 开发一个【GIF表情包制作神器】
    用python成为了微信斗图届的高手然后,好多人表示:虽然存了很多表情包但似乎还不是很过瘾因为它不可以自己来定制我们可不可以根据一些表情素材然后自己制作专属表情包呢像这样本来小帅b想自己实现一个表情包制作器后来发现已经有人在GitHub 分享了   主要功能就是可以......
  • 使用 python matplotlib 将 LaTex 公式转为 svg
    使用pythonmatplotlib将LaTex公式转为svg,从而方便插入无法打出所需公式的ppt中。importmatplotlib.pyplotaspltdeflatex_formula2svg(text,font_size=12,save_fig='formula.svg'):plt.rc('text',usetex=True)#使用LaTeX渲染文本plt.rc('f......
  • Python连接访问mongodb副本集
    代码如下:frompymongoimportMongoClient#配置副本集的地址replica_set_hosts=["192.168.10.135:27017","192.168.10.136:27018","192.168.10.137:27019"]#创建MongoClient连接client=MongoClient(replica_set_hosts,userna......
  • python3使用dpkt生成PCMA格式rtp流
    操作系统:CentOS7.6_x64Python版本:3.9.12dpkt版本:1.9.8PCMA编码是VoIP通信中常见的格式,今天整理下CentOS7环境下,python3如何使用dpkt生成PCMA格式rtp流的笔记,并提供相关示例代码、运行效果视频和配套文件下载。我将从以下几方面进行展开:背景材料使用dpkt生成PCMA格式rt......
  • 推荐一个教程,适用于想学python但是只学点基础知识用于刷题的
    省流:https://www.bilibili.com/video/BV1Lk4y117Cb?p=1&vd_source=4a339d299e165d8fe38b9926c5240eae我以前一直使用Java刷题,但是随着刷题的数量越来越多,越发感觉Java真的不适合用来刷题,看leetcode里面的大佬们基本都是清一色的c++和python,所以我也是想学点python用于刷题,花了......
  • 用python写一个 将指定目录下以及其下所有子目录下的srt文件复制一份并重命名带上文件
    代码:importosimportshutildefcopy_and_rename_files(src_directory,target_directory):#确保目标目录存在ifnotos.path.exists(target_directory):os.makedirs(target_directory)#遍历指定目录及其所有子目录forroot,dirs,file......
  • Python学习之路 第五篇 基本数据类型
    int类型:在python3里不论数有多大,永远都是int类型。在python2里整形(数字),在范围内叫int,超出范围叫long,也叫长整型。在python3里所有整形(数字)的功能都包含在int里。int功能展示:输入int摁住ctrl键然后同时将鼠标箭头放在int上出现小手后点击进去就能看到int所具有的功能。表示所有的数......
  • Java多线程
    程序,进程,线程程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;进程是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位;通常在一个进程中可以包含若干个线程,线程是CPU调度和执行的单位;若是单核cpu,则多线程是模拟出来的,在一个cpu的情况下,在同......