Python爬虫:获取菜价——线程池的使用

目录

一、发起请求+获取响应数据

二、处理数据

三、存储文件

四、上线程池获取所有数据

(一)循环执行

(二)上线程池

(三)对比时间

 五、代码


异步的逻辑:

1.获取单个页面的数据
2.上线程池,多个页面同时获取

一、发起请求+获取响应数据

        尝试对单个页面发起请求,然后使用for循环循环遍历每个页面,使用三个请求头的列表进行UA伪装随机发起请求

(我访问的网站并没有反爬,所以这一步跟随机休眠可以省略)

import requests
import random
def single_page(url):
    user_agent_list = [
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.200']

    head = {'user-agent': random.sample(user_agent_list, 1)[0]}

    parm={'limit': '20',
            'current': '1',
            'pubDateStartTime': '',
            'pubDateEndTime': '',
            'prodPcatid': '',
            'prodCatid': '',
            'prodName': ''}

    resp=requests.post(url,params=parm,headers=head).json()

        该网站发起 Post 请求返回的是 Json() 形式的数据,在网页查看比较清晰明了,是这样的结构:

二、处理数据

        直接得到了Json数据,就不需要用正则啥的想办法处理源码了,直接当做列表字典来处理,我们想要的数据在响应数据的 'list' 键里面,一页20个数据都以字典形式在 list 里

    for k in range(len(resp['list'])):
        print([resp['list'][k]['prodCat'],
              resp['list'][k]['prodName'],
              resp['list'][k]['lowPrice'],
              resp['list'][k]['highPrice'],
              resp['list'][k]['avgPrice'],
              resp['list'][k]['prodName'],
               resp['list'][k]['place'],
               resp['list'][k]['unitInfo'],
               resp['list'][k]['pubDate']])

        这里刚开始报错: k 是字典数据的位置索引,由于 list 里面存着一个20个字典的列表,通过对应键取值的时候,需要知道是哪个字典里的键

['蔬菜', '大白菜', '0.4', '0.6', '0.5', '大白菜', '冀', '斤', '2023-08-17 00:00:00']
['蔬菜', '娃娃菜', '0.7', '1.0', '0.85', '娃娃菜', '冀', '斤', '2023-08-17 00:00:00']
...
['蔬菜', '绿菜花', '1.4', '2.0', '1.7', '绿菜花', '冀', '斤', '2023-08-17 00:00:00']
['蔬菜', '绿豆芽', '0.95', '1.0', '0.98', '绿豆芽', '', '斤', '2023-08-17 00:00:00']

        补充:页面有如下这种数据,这里的 '袋\箱' 里的 '\' 号可能导致程序出错,所以有时候可能需要替换或删除(用 str.replace)

三、存储文件

        我将获得的数据写入csv文件(以下除了 import 写在定义任务的第一行)

        这里模式必须是 'a' 而不能是 'w'!!!因为 'w' 模式每次写入都会覆盖掉上一次的内容,无法获得全部数据

import csv

    data_table=open (r'C:\Users\yysc\Desktop\data_table.csv',mode='a',newline = '')
    csv_writer=csv.writer(data_table) 

    # 实例化一个writer,将一行行data数据写入data_table

         再在for循环里加上写入文件的操作

    for k in range(len(resp['list'])):
        data=[resp['list'][k]['prodCat'],
              resp['list'][k]['prodName'],
              resp['list'][k]['lowPrice'],
              resp['list'][k]['highPrice'],
              resp['list'][k]['avgPrice'],
              resp['list'][k]['prodName'],
               resp['list'][k]['place'],
               resp['list'][k]['unitInfo'],
               resp['list'][k]['pubDate']]
        csv_writer.writerow(data)
        # 一行行写入,writerows是一次写入多行,这里用writerows表格会变得非常混乱
    print('第'+parm['current']+'页提取完毕')

第1页提取完毕

        运行程序

if __name__ == '__main__':
    single_page(url='http://www.xinfadi.com.cn/getPriceData.html')

        成功获取了第一页的数据,我的目录是存在桌面的

        关于csv文件处理查看:

Python 使用csv库处理CSV文件_python csv库_一杯冰糖的博客-CSDN博客

四、上线程池获取所有数据

(一)循环执行

        每次换页面的时候网页 url 其实并没有改变,只是参数 current 改变了

         因此我将 parm 里面的 current 改为动态的,在每次发起请求时传入页面的参数,就能访问到不同页面了

def single_page(url,page):

    parm={'limit': '20',
            'current': page,    # 该参数决定页面
            'pubDateStartTime': '',
            'pubDateEndTime': '',
            'prodPcatid': '',
            'prodCatid': '',
            'prodName': ''}

        接下来循环执行程序,一共有25667页,则range(1,20668),页面是从第一页开始的没有第0页,左闭右开,运行时记得关闭文件!!

if __name__ == '__main__':
    for i in range(1,10):
        single_page(url='http://www.xinfadi.com.cn/getPriceData.html',
                    page=str(i))
                    # 传入url及page参数

(二)上线程池

        提交任务给50个线程,submit的时候参数是通过逗号连接的

from concurrent.futures import ThreadPoolExecutor

if __name__ == '__main__':
    with ThreadPoolExecutor(50) as t:
        for i in range(1,100):
        t.submit(single_page,url='http://www.xinfadi.com.cn/getPriceData.html',page=str(i))

(三)对比时间

        使用 time.perf_counter() 可以通过开始、结束的时间计算程序运行的时间,看出效率的提升情况,看看前一百页提取情况

import time

if __name__ == '__main__':
    start_time_1=time.perf_counter()
    for i in range(1,100):
        single_page(url='http://www.xinfadi.com.cn/getPriceData.html',page=str(i))
    end_time_1=time.perf_counter()
    times_1=end_time_1-start_time_1

    print('Mission I All Finshed!!Spent '+str(times_1))

    # 加线程池
    start_time_2=time.perf_counter()
    with ThreadPoolExecutor(50) as t:
        for i in range(1,100):
            t.submit(single_page,url='http://www.xinfadi.com.cn/getPriceData.html',page=str(i))
    end_time_2=time.perf_counter()
    times_2=end_time_2-start_time_2

    print('Mission II All Finshed!!Spent '+str(times_2))
...
第99页提取完毕
Mission I All Finshed!!Spent 25.265385799999997

...
第90页提取完毕
Mission II All Finshed!!Spent 3.0848887000000005

         效率提升高了很多倍

        后续看到家线程池文件里面有些地方是混乱的,原因可能跟CPU本身的调度有关,是不过不是此次探究的侧重点,暂不理会

 五、代码

import requests
import random
import csv
import time
from concurrent.futures import ThreadPoolExecutor
def single_page(url,page):
    data_table = open(r'C:\Users\yysc\Desktop\data_table.csv', mode='a', newline='')
    csv_writer = csv.writer(data_table)  # 实例化一个writer,将一行行data数据写入data_table

    user_agent_list = ['Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
                        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
                        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.200']
    head = {'user-agent': random.sample(user_agent_list, 1)[0],
            'Referrer Policy':'strict - origin - when - cross - origin'
    }
    parm={'limit': '20',
            'current': page,
            'pubDateStartTime': '',
            'pubDateEndTime': '',
            'prodPcatid': '',
            'prodCatid': '',
            'prodName': ''}

    resp=requests.post(url,params=parm,headers=head).json()

    for k in range(len(resp['list'])):
        data=[resp['list'][k]['prodCat'],resp['list'][k]['prodName'],resp['list'][k]['lowPrice'],resp['list'][k]['highPrice'],resp['list'][k]['avgPrice'],resp['list'][k]['prodName'],resp['list'][k]['place'],resp['list'][k]['unitInfo'],resp['list'][k]['pubDate']]
        csv_writer.writerow(data) # 一行行写入,writerows是一次写入多行
        print(data)
    print('第'+parm['current']+'页提取完毕')

    data_table.close()
pass


if __name__ == '__main__':
    start_time_1=time.perf_counter()
    for i in range(1,6):
        single_page(url='http://www.xinfadi.com.cn/getPriceData.html',page=str(i))
    end_time_1=time.perf_counter()
    times_1=end_time_1-start_time_1

    print('Mission I All Finshed!!Spent '+str(times_1))

    start_time_2=time.perf_counter()
    with ThreadPoolExecutor(50) as t:
        for i in range(1,100):
            t.submit(single_page,url='http://www.xinfadi.com.cn/getPriceData.html',page=str(i))
    end_time_2=time.perf_counter()
    times_2=end_time_2-start_time_2

    print('Mission II All Finshed!!Spent '+str(times_2))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

带带琪宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值