piazini 发表于 2022-1-30 19:49

多线程 爬取网页历史版本文件[源码]

本帖最后由 piazini 于 2022-1-30 20:00 编辑

小技巧:启动Python默认自带的http服务器(在那个目录启动,当前路径就http服务的/根目录)
Python3:
python -m http.server 8000
Python2:
python2 -m SimpleHTTPServer

Python3 "多线程爬取历史文件"代码:
#!/usr/bin/env python3
#_*_ encoding:UTF-8 _*_
# DATE: 2022/01/20

import requests
from multiprocessing import Process,Queue
import time
import os,sys

## 文件版本
file_version = '1.0.2'

## 开始、结束日期
date_start = '0601'
date_end = '0531'

## 最大线程数。建议不超过”任务管理器--性能“里显示总数-1,
##      -1是保留一个进程给其他程序用,比如显示4个,4-1=3
queue_maxsize = 2

## 全局变量
_list_month_day = []
global _flag_creat_count
_flag_creat_count = 0
global _flag_down_count
_flag_down_count = 0

## 检查设置的线程数是否超过CPU的数量
def check_cpu_count():
    os_cpu_count = os.cpu_count()
    if os_cpu_count < queue_maxsize:
      print("\n[ Warning ] CPU支持[%d]个线程,设置线程queue_maxsize=%d,建议设置[%d],小于CPU线程数。\n"\
      %(os_cpu_count,queue_maxsize,os_cpu_count-1))

## 判断date_start_month月有多少天,并设置date_start_month月的天数
def set_month_days(date_start_month):
    month = date_start_month
    ## 是31天的月份
    month_31day = "01030507081012"
    ## 是30天的月份
    month_30day = "04060911"

    ## 默认是28天
    day = 28
    ## 判断month_31day是否包含传入的month月份,为真,这月是31天
    if month in month_31day:
      day = 31
    ## 判断month_30day是否包含传入的month月份,为真,这月是30天
    elif month in month_30day:
      day = 30
    return day
   
##产生月和日
def add_month_day():
    date_start_month = date_start
    date_start_day = date_start
    ## 如果月的第一位是0则取第二位
    if date_start_month[:1] == "0":
      date_start_month = date_start_month
    ## 如果日的第一位是0则取第二位
    if str(date_start_day)[:1] == "0":
      date_start_day = date_start_day
      
    ## 循环产生月和日
    while True:
      ## 将上一次循环设置成int类型,转回str类型
      date_start_month = str(date_start_month)   
      date_start_day = str(date_start_day)
      
      ## 如果日等于1号,则月份减一,
      ##并调用set_month_days函数,获取本月天数
      if date_start_day == "1":
            ## 月减一
            date_start_month = int(date_start_month) - 1
            date_start_day = set_month_days(str(date_start_month))
      else:
            ## 天减一
            date_start_day = int(date_start_day) - 1

      ##month_day值:1.拼接月+日,不足两位,左补0
      ##            2.上面运算把月和日转成int型,拼接需要转str型
      month_day = "{:0>2}".format(eval(str(date_start_month))) + "{:0>2}".format(eval(str(date_start_day)))
      
      ## 将生成的月和日添加到全局变量列表中,方便其他类或方法使用
      global _list_month_day
      _list_month_day.append(month_day)
      
      ## 如果当前月日和设置的date_end相等,则退出while循环
      if date_end == month_day:
            break
   
## 生成访问的url连接
def create_url(q):
    global _flag_creat_count
   
    ## 调用生成日期函数
    add_month_day();
   
    ## 循环生成
    while True:
      ## 月日
      for m_day in _list_month_day:
            ## 小时
            for h in range(0,24):
                ## 分钟
                for m in range(0,59):
                  ## 拼接生成下载地址
                  # url = 'https://127.0.0.1:8000/' + file_version + '/2021' +'%s%02d%02d'%(m_day,h,m) + '/tv_' + file_version + '.apk'
                  url = 'http://127.0.0.1:8000/' + file_version + '/2021' +'%s%02d%02d'%(m_day,h,m) + '/tv_' + file_version + '.apk'
                  q.put(url)
                  _flag_creat_count = _flag_creat_count + 1
      break   
    print(f"总共放入{_flag_creat_count}个URL")
    return _flag_creat_count
            
def download_file(q):
    global _flag_down_count
   
    while True:
      url = q.get()
      print(f'正在连接: ' + url)
      r = requests.get(url)
      codes = r.status_code
      
      ## 访问网站返回值为200说明文件存在
      if codes == 200:
            print("找到文件: " + url )
            print(">>> 记录到sucess.txt文件中")
            ## 参数a,追加内容到文件中
            with open ('sucess.txt','a') as f:
                f.write(url + '\n')
   
if (__name__ == 'main') or (__name__ == '__main__'):
    ## 记录开始时间
    #t1 = time.time()
    ## 检查设置的线程数是否超过CPU的数量
    check_cpu_count();
    print("\n提示: 用Ctrl + C 结束脚本\n")
    print("创建{%d}个线程..." %queue_maxsize)
    q = Queue(maxsize=queue_maxsize)
    c = Process(target=create_url,args=(q,))
    d = Process(target=download_file,args=(q,))
    d.start()
    c.start()
    #print("阻塞线程d")
    d.join()
    #print("阻塞线程c")
    c.join()
    ## 记录结束时间
    #t2 = time.time()
    #print(f"总耗时:{t2 -t1}")
   
   

脚本执行效果图:


Python Http服务日志:


还有个问题,多线程脚本启动后,设定链接访问完后,也只能手动用Ctrl+C手动停止,
希望大佬赐教,应该怎么改,能让脚本访问完链接后退出。
尝试用“标记flag”和“记录产生链接数和访问链接数相等时退出”也没成功

__EOF__

ych13846701169 发表于 2022-1-30 23:48

好,但是不会呀

cxb1998 发表于 2022-1-31 00:08

这个有用诶 有的时候想找但是又没有记录了

a3322a 发表于 2022-1-31 04:06

感谢分享,学习了

52896009 发表于 2022-1-31 12:52

但是不会呀

piazini 发表于 2022-2-2 10:07

ych13846701169 发表于 2022-1-30 23:48
好,但是不会呀

没关系,慢慢学:Dweeqw

piazini 发表于 2022-2-2 10:08

cxb1998 发表于 2022-1-31 00:08
这个有用诶 有的时候想找但是又没有记录了

是的,我也是因为有这个需求才写的这个

piazini 发表于 2022-2-2 10:09

a3322a 发表于 2022-1-31 04:06
感谢分享,学习了

谢谢,互相学习

piazini 发表于 2022-2-2 10:10

52896009 发表于 2022-1-31 12:52
但是不会呀

互相学习,慢慢来
页: [1]
查看完整版本: 多线程 爬取网页历史版本文件[源码]