爬虫实战——何时使用多线程,何时使用多进程

进程

运行中的程序,独立的运行程序,进程是资源的基本最小单位,有独立内存,是并行操作。

多进程创建

from multiprocessing import Process
from datetime import datetime

def func(name):
    for i in range(100):
        print(i, "===", name)
    return name

# 创建进程
p1 = Process(target=func, args=("周杰伦",))
p2 = Process(target=func, args=("李克勤",))


# 执行
p1.start()
p2.start()

同时发生任务,并行。

线程

线程是cpu调度的基本最小单位,cpu调动最小资源,通过线程执行资源,一个进程可以拿着资源开若干个线程,不同线程之间交替执行。

4个线程同时进行

from threading import Thread


# 创建任务
def func(name):
    for i in range(10000):
        print(i, "===", name)


# 创建线程
t1 = Thread(target=func, args=("周杰伦",))
t2 = Thread(target=func, args=("李克勤",))
t3 = Thread(target=func, args=("那英",))
t4 = Thread(target=func, args=("你好",))


# 执行
t1.start()
t2.start()
t3.start()
t4.start()

在这里插入图片描述

面向对象多线程程序

继thread并重写Thread的run方法

from threading import Thread

class MyThread(Thread):
    def __init__(self, name):
        # 继承参数
        super(MyThread, self).__init__()
        self.name = name
    
    # 重写run方法
    def run(self):
        for i in range(10000):
            print(i, "===", self.name)

# 创建线程
t1 = MyThread("周杰伦")
t2 = MyThread("李克勤")
t3 = MyThread("那英")
t4 = MyThread("你好")

# 执行
t1.start()
t2.start()
t3.start()
t4.start()

在这里插入图片描述

线程池

线程是不能随便创建的,所以需要控制线程数量,线程池可以完成这项工作

无返回
from concurrent.futures import ThreadPoolExecutor

# 创建任务
def func(name):
    for i in range(10000):
        print(i, "===", name)

# 创建线程池
with ThreadPoolExecutor(10) as t:
    for i in range(10):
        t.submit(func, "周杰伦"+'=='+str(i))

在这里插入图片描述

有返回值
  • 方法1
from concurrent.futures import ThreadPoolExecutor


# 创建任务
def func(name):
    print(i, "===", name)
    return name


# 创建线程池 并获取返回值
# add_done_callback 任务完成之后立即执行
with ThreadPoolExecutor(10) as t:
    t.submit(func, "周杰伦").add_done_callback(lambda res: print(res.result()))
    t.submit(func, "李克勤").add_done_callback(lambda res: print(res.result()))
    t.submit(func, "那英").add_done_callback(lambda res: print(res.result()))
  • 方法2
from concurrent.futures import ThreadPoolExecutor


# 创建任务
def func(name):
    print(i, "===", name)
    return name


# 创建线程池 并获取返回值
# add_done_callback 任务完成之后立即执行
with ThreadPoolExecutor(10) as t:
    result = t.map(func, ["周杰伦", "李克勤", "那英"])
print(list(result))

何时使用多线程,何时使用多进程

多线程:任务相对统一,互相特别相似

多进程:多个任务相互独立,很少有交集

比如:免费ip池

  • 1、从免费网站抓取代理ip
  • 2、验证ip是否可用
  • 3、准备对外的接口

财 务 部 政 策 实战

# 整合版
import json
import os
import re
import time
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timedelta
from multiprocessing import Process, Queue
from urllib.parse import urljoin

import numpy as np
import pandas as pd
import requests
from pyquery import PyQuery

head = {
    "User-Agent": np.random.choice(
        [
            "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
            "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/13.0.782.215)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/11.0.696.57)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) chromeframe/10.0.648.205",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.1; SV1; .NET CLR 2.8.52393; WOW64; en-US)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0; chromeframe/11.0.696.57)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/4.0; GTB7.4; InfoPath.3; SV1; .NET CLR 3.1.76908; WOW64; en-US)",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A",
            "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
            "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
        ]
    )
}


def get_today_and_lastday():
    t = datetime.now()
    oneday = timedelta(days=1)
    yesterday = t - oneday
    return datetime(year=t.year, month=t.month, day=t.day), datetime(
        year=yesterday.year, month=yesterday.month, day=yesterday.day
    )


## 1、抓取信息网页
def get_li_list(url, end_time, queue, page=0):
    if page != 0:
        crawl_url = url + "_%s" % page + ".htm"
    else:
        crawl_url = url + ".htm"

    response = requests.get(crawl_url, headers=head)
    response.encoding = "utf-8"
    pq = PyQuery(response.text)

    li_info_list = [
        [urljoin(crawl_url, i("a").attr("href")), i("a").text(), i("span").text()]
        for i in list(pq("ul.xwfb_listbox > li").items())
    ]

    # 获取当页最早文章发布时间
    publish_time = datetime.strptime(li_info_list[-1][-1], "%Y-%m-%d")

    # 如果时间时间小于截至时间,停止
    if publish_time < end_time:
        return li_info_list

    # 否则翻页,递归执行
    page += 1
    li_info_list += get_li_list(url, end_time, queue, page)

    for i in li_info_list:
        if (
            datetime.strptime(i[-1], "%Y-%m-%d") >= end_time
            and datetime.strptime(i[-1], "%Y-%m-%d") < begin_time
        ):
            queue.put(i)
    return li_info_list


## 多线程作业
def get_url(q, end_time):
    url_list = [
        "https://www.mof.gov.cn/zhengwuxinxi/caizhengxinwen/index",
        "https://www.mof.gov.cn/zhengwuxinxi/zhengcefabu/index",
        "https://www.mof.gov.cn/zhengwuxinxi/zhengcejiedu/index",
    ]
    with ThreadPoolExecutor(num_executor) as t:
        for url in url_list:
            t.submit(get_li_list, url, end_time, q, page=0)


# 第二步多线程抓取网页信息
def insert_db_data(data):
    response = requests.get(data[0], headers=head)
    response.encoding = "utf-8"
    pq = PyQuery(response.text)

    db_insert_data.append(
        [
            # crawl_time 爬取时间
            datetime.now(),
            # publish_time 发布时间
            datetime.strptime(data[-1], "%Y-%m-%d"),
            # 原始网址
            data[0],
            # 网站模块
            pq("div.dangqian").text().split(">")[-1],
            # 标题
            pq("div.box_content > h2.title_con").text().replace("\n", ""),
            # 作者或来源
            "中华人民共和国财政部",
            # 文章内容
            pq("div.TRS_Editor >p").text(),
            # 附件 存储附件地址 media/app03/data/
            "",
        ]
    )
    # (pq("div.box_content > h2.title_con").text(), ">>>>>", "插入队列") 可打印


def get_url_info(
    q,
):
    with ThreadPoolExecutor(num_executor) as t:
        while True:
            if q.empty():
                break
            data = q.get()
            t.submit(insert_db_data, data)


## main函数
def spider():
    get_url(q, end_time)
    get_url_info(q)
    return db_insert_data


## 全局函数
q = Queue()
begin_time = datetime.now()
end_time = datetime(2023, 10, 1)
db_insert_data = []
num_executor = 5
spider()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值