Python多进程模块实战: 下载某小说网小说内容

练习一下Python的multiprocessing模块,成品代码如下:

#!/usr/bin/python3
# 书站爬虫
# 仅限学习多进程机制使用 使用后果全部由使用者自行承担.
import requests
import parsel
from tqdm import tqdm
from time import sleep
import os
from multiprocessing import Process, Value

# 多进程中共享的变量
# 使用multiprocessing自带的Value
q = Value('d', 0) #进程数
status = Value('d', 0) #运行锁

def new_sub_thread(func, args=()):
 # 多进程,启动!
 thread = Process(target=func, args=args, daemon=True)
 thread.start()

def get_response(html_url, text_mode):
 headers = {
 'User-Agent': 'Wget/1.21.1',
 'accept-language': 'zh-CN,zh'
 }
 try:
  response = requests.get(url=html_url, headers=headers)
 except:
  # 失败重试(小的网络波动)
  sleep(2)
  try:
    response = requests.get(url=html_url, headers=headers)
  except:
    # 第二次重试(书站特有dns污染)
    sleep(4)
    response = requests.get(url=html_url, headers=headers)
 if text_mode == True:
    # 书站特有编码问题 标题和正文的编码不一样...
    response.encoding = response.apparent_encoding
 return response

def save(novel_name, title, content):
 # 原始的保存机制...
 filename = f'{novel_name}' + '.txt'
 with open(filename, mode='w', encoding='utf-8') as f:
 # 写入标题
    f.write(title)
 # 换行
    f.write('\n')
 # 写入小说内容
    f.write(content)
 # 换行
    f.write('\n')

def get_one_novel(name, novel_url, q, k, fulllen, status):
 # 下载某一章节的内容
 #name: 小说名/ID, novel_url: 单章url, q: (共享)当前进程数, k: 下载章数, status: (共享)状态 0/1
 q.value=q.value+1 #进程数+1
 # 调用请求网页数据函数
 response = get_response(novel_url, True)
 # 转行成selector解析对象
 selector = parsel.Selector(response.text)
 # 获取小说标题
 title = selector.css('.bookname h1::text').get()
 # 获取小说内容 返回的是list
 content_list = selector.css('#content::text').getall()
 # '\n'.join(列表) 把列表转换成字符串 并且带上换行
 content_str = '\n'.join(content_list)
 save(name+'_tmp/'+str(k)+'_'+name, title, content_str)
 q.value=q.value-1 #进程数-1
 if k == fulllen:
     status.value=1 #解运行锁

def get_all_url(html_url, novel_id, isreplace):
 # 调用请求网页数据函数
 response = get_response(html_url, False)
 # 转行成selector解析对象
 selector = parsel.Selector(response.text)
 # 所有的url地址都在 a 标签里面的 href 属性中
 dds = selector.css('#list dd a::attr(href)').getall()
 # 小说名字 同时检测小说是否存在
 try:
  novel_name = selector.css('meta[property="og:title"]::attr(content)').get()
 except:
  print("这本小说不存在...请检查ID是否正确")
  raise SystemExit() #手动退出
 print("ID: "+str(novel_id)+" 标题: "+novel_name+" 章数: "+str(len(dds)))
 k = 1 #从1开算
 if isreplace == 1:
     novel_id = novel_name
 os.system(f'mkdir {novel_id}_tmp') #缓存目录
 for dd in tqdm(dds):
     while q.value == 8:
         # max 八进程 多了会等待结束一个进程后再继续
         sleep(0.2)
     fulllen = len(dds)
     novel_url = base_url + dd
     new_sub_thread(get_one_novel, (novel_id, novel_url, q, k, fulllen, status))
     sleep(0.2)
     k = k+1
 while status.value == 0:
    sleep(1) #死循环直到跑完
 while q.value != 0:
    sleep(0.5) #等待跑完
 file1 = open(f'{novel_id}.txt', 'w')
 for i in range(1, len(dds)+1):
     f = open(f'{novel_id}_tmp/{i}_{novel_id}.txt')
     file1.write(f.read())
     f.close()
 file1.close()
 os.system(f'rm -r {novel_id}_tmp')
 print("下载完成!")

def main():
 novel_id = input('输入锡海小说网的ID: ')
 global base_url
 #base_url = input('输入主站Url(eg: https://www.otcwuxi.com): ')
 base_url = 'https://www.otcwuxi.com'
 print("主站Url已默认: https://www.otcwuxi.com")
 url= base_url + '/chapter/' + novel_id + '/'
 isreplace = 1 #是否将小说文件名改为小说名字
 get_all_url(url, novel_id, isreplace)

if __name__ == '__main__':
 main()

仅限学习多进程机制使用,使用后果由使用者自行承担。因特殊原因本文章不接受评论。

posted @ 2023-09-10 07:45  星如雨yu  阅读(127)  评论(0编辑  收藏  举报