Pythony——多线程简单爬虫实现

简单爬虫实现

import requests
from bs4 import BeautifulSoup

# 生成要爬取的网页地址列表,这里是博客园的分页地址,从第1页到第50页
urls = [f"https://www.cnblogs.com/#p{i}" for i in range(1, 50 + 1)]


# 生产者函数——负责下载网页内容
def craw(url):
    这个函数接收一个网页的URL作为参数,使用requests库发送GET请求获取该网页的内容,并以文本形式返回。
    r = requests.get(url)# 要下载内容的网页URL
    return r.text#返回获取到的网页文本内容

# 消费者函数——拿着生产者下载好的网页内容,进行解析
def parse(html):
    """
    此函数接收从网页下载的HTML文本内容作为参数,使用BeautifulSoup库对其进行解析,
    提取出所有class名为post-item-title的超链接a标签,然后遍历这些标签,将每个标签的href链接内容
    和标签文字内容以元组的形式提取出来并返回
    """
    # 指定为html解析器 从网页上下载下来的HTML文本内容
    soup = BeautifulSoup(html, "html.parser")  

    # 提取出所有class名为post-item-title 的 超链接 a标签
    links = soup.find_all("a", class_="post-item-title")
    # 遍历links元组,将超链接a标签的href链接内容 和 标签文字内容提取输出
    
    return [(link["href"], link.get_text()) for link in links]
    # 返回一个包含元组的列表,每个元组包含一个超链接的href值和对应的文字内容


# 主程序入口,当脚本直接运行时执行以下代码
if __name__ == '__main__':
    """
    在这里我们调用了craw函数去下载urls列表中第3个网址(索引为2)的网页内容,
    然后将下载好的内容传递给parse函数进行解析,最后遍历parse函数返回的结果并打印出来。
    """
    for result in parse(craw(urls[2])):
        print(result)

生产者消费者函数

import threading
import time
import random
import queue
import blog_spider

# 定义生产者线程要执行的函数,用于从URL队列中获取URL,爬取网页内容并放入HTML队列
def do_craw(url_queue: queue.Queue, html_queue: queue.Queue):#url_queue: 存储待爬取URL的队列, html_queue: 用于存储爬取到的网页HTML内容的队列
 
    """
    这个函数是生产者线程要执行的函数。
    不断从URL队列中获取URL,调用blog_spider.craw函数爬取网页的内容,
    然后将爬取到的HTML内容放入HTML队列中,并打印相关的内容。
  
    """
    while True:
        # 从URL队列中获取一个URL
        url = url_queue.get()
        # 调用blog_spider模块中的craw函数爬取该URL对应的网页内容
        html = blog_spider.craw(url)
        # 将爬取到的网页内容放入HTML队列
        html_queue.put(html)
        # 打印当前线程的名称、正在爬取的URL以及URL队列剩余的大小
        print(threading.current_thread().name, f"craw {url}", "url_queue.size=", url_queue.qsize())
        time.sleep(random.randint(1, 2))#休眠一段时间

# 定义消费者线程要执行的函数,用于从HTML队列中获取网页内容
def do_parse(html_queue: queue.Queue, fout):#html_queue: 存储网页HTML内容的队列
    """
    此函数是消费者线程要执行的任务函数。
    它会不断从HTML队列中获取网页内容,调用blog_spider.parse函数对其进行解析,fout: 用于写入解                    
析结果的文件对象
    将解析结果写入到指定的文件中,并打印相关的内容。
    """
    while True:
        # 从HTML队列中获取一个网页内容
        html = html_queue.get()
        # 调用blog_spider模块中的parse函数对网页内容进行解析,得到解析结果
        results = blog_spider.parse(html)
        for result in results:
            fout.write(str(result) + "\n")
        # 打印当前线程的名称、解析结果的数量以及HTML队列剩余的大小
        print(threading.current_thread().name, f"results.size", len(results), "html_queue_size=", html_queue.qsize())
        time.sleep(random.randint(1, 2))#休眠一段时间

# 主程序入口,当脚本直接运行时执行以下代码
if __name__ == '__main__':
    # 创建一个用于存储待爬取URL的队列
    url_queue = queue.Queue()
    # 创建一个用于存储爬取到的网页HTML内容的队列
    html_queue = queue.Queue()

    # 将blog_spider模块中定义的所有URL放入URL队列
    for url in blog_spider.urls:
        url_queue.put(url)

    # 开启生产者线程
    for idx in range(3):
        # 创建一个新的线程,指定其执行的任务函数为do_craw,并传入相应的参数
        t = threading.Thread(target=do_craw, args=(url_queue, html_queue), name=f"craw{idx}")
        t.start()

    # 开启消费者线程和创建用于存储解析结果的文本文件
    fout = open("spider_data.txt", "w")
    for idx in range(2):
        # 创建一个新的线程,指定其执行的任务函数为do_parse,并传入相应的参数
        t = threading.Thread(target=do_parse, args=(html_queue, fout), name=f"parse{idx}")
        t.start()

### Python 函数使用教程 #### 定义函数 在 Python 中,`def` 关键字用于定义函数。一个简单的例子如下所示: ```python def greet(name): """这是一个简单的问候函数""" print(f'你好, {name}') ``` 此段代码展示了如何创建接受单个参数 `name` 的函数,并打印一条消息[^3]。 #### 参数传递 Python 支持多种方式向函数传递参数,包括位置参数、关键字参数,默认参数值等。下面是一个带有默认参数的例子: ```python def greet_with_default(name="世界"): print(f'你好, {name}') greet_with_default() greet_with_default("Alice") ``` 这段代码说明了当调用者未提供特定实参时,将采用设定好的默认值。 #### 返回多个值 不同于某些其他编程语言,在 Python 中可以通过元组轻松实现多值返回: ```python def get_name_and_age(): name = "张三" age = 28 return name, age # 或者写成 (name, age) person_info = get_name_and_age() print(person_info) ``` 上述实例表明了一个函数可以同时给出两个甚至更多结果给接收方[^1]。 #### Lambda 表达式 对于只需要简单操作的小型匿名函数来说,Lambda 是非常方便的选择: ```python double = lambda x: x * 2 result = double(5) print(result) ``` 这里展示了一种简洁的方式来构建仅需一次使用的临时函数对象。 #### 装饰器应用 装饰器本质上也是函数,它允许程序员修改另一个函数的行为而不改变其源码。例如日志记录或性能计时等功能都可以通过装饰器来完成: ```python from functools import wraps import datetime def log_execution_time(func): @wraps(func) def wrapper(*args, **kwargs): start_time = datetime.datetime.now() result = func(*args, **kwargs) end_time = datetime.datetime.now() print(f"{func.__name__} 执行时间:{end_time - start_time}") return result return wrapper @log_execution_time def some_function_to_measure_performance(x): sum(range(x)) some_function_to_measure_performance(1000000) ``` 以上代码片段解释了怎样利用装饰器模式增强现有功能而无需侵入性更改原有逻辑结构[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值