最新python爬虫

一次python爬取 笔趣看小说网 的记录

相关技术

            python, lxml, requests, 消息队列,多线程(开了一百个线程,小说下载速度超快)

项目结构

         自己构建了一个IP代理池,将不干净的 (混乱IP.txt) 文件中的IP清洗过后,写入到(可用的IP.txt)文件中

爬虫有一定的时效性,刚写好的爬虫,说不定过一天就失效了,所以要看就看最新的吧,笔趣看小说网 会封IP的,单个IP去爬取

下载速度会越来越慢,所以必须提前准备好IP代理。

          网站首页:         https://www.biqukan.com

注意:该网站采用  gbk 编码,页面中充斥着大量   \0a0, \r   等字符, \r  会让光标回到行首,从而导致后面的内容覆盖前面的内容

项目的 gitee 地址: https://gitee.com/o1uncle/spyder_biqukan

主要代码如下:

# -*- coding:utf-8 -*-
"""
开发者:o1uncle
日 期:2020 年 07 月 12 日
description:---*- 爬取笔趣看小说网 -*---
    多线程,IP 代理
思路:
    1:获取小说的所有章节链接
    2:将章节 (章节号 + 链接) 全部 put 到队列 urls_queue 中,章节号用于下载完成后的排序
    3:重写线程,构造run(), 从 urls_queue 中 get, 处理好之后将(章节号 + 章节内容)  put 到 wait_queue
    4:wait_queue 队列中的数据,放到一个列表中,排序
    5:排序之后将列表写到文件中

不足:这里一次只能下载固定链接的一本小说
"""
import requests
from lxml import etree
import random
from queue import Queue
from threading import Thread

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
}

ip_pool = []  # IP 代理池
current_progress = 0
novel_name = 'none'  # 小说的名字


# 构造 IP 代理池
def creat_ip_pool() -> list:
    global ip_pool
    with open('../3-2-available-ip.txt', 'rt') as f:
        while True:
            ip = f.readline()
            if ip == '':
                # return ip_pool
                break
            ip_pool.append(ip.strip('\n'))


def all_urls_enter_queue(urls_queue: Queue, aurl='/1_1680/'):  # 下载一本小说
    global headers
    global novel_name
    url = 'https://www.biqukan.com'
    req = requests.get(url + aurl, headers)
    req.encoding = 'gbk'
    e = etree.HTML(req.text)
    # 得到所有的章节链接
    sk = e.xpath('//h2/text()')
    novel_name = sk[0]
    result = e.xpath('//dd[position()>12]/a/@href')
    # ['/1_1680/16813635.html', '/1_1680/16813636.html',
    # 将(章节号+链接)全部压入队列
    for i in range(len(result)):
        urls_queue.put((i, url + result[i]))  # 'https://www.biqukan.com/1_1680/16813636.html'

    # for i in range(100): # 下载一部分链接,用于调试,以免对服务端产生太大压力
    #     urls_queue.put((i, url + result[i]))  # 'https://www.biqukan.com/1_1680/16813636.html'


class DownloadNovel(Thread):
    def __init__(self, urls_queue: Queue, wait_queue: Queue):
        self.urls_queue = urls_queue
        self.wait_queue = wait_queue
        Thread.__init__(self)

    def download_one_chapter(self, url):
        # 发起网络请求
        ip = random.choice(ip_pool)
        proxies = {'http': 'HTTP://{}'.format(ip)}
        while True:
            try:
                req = requests.get(url, headers=headers, proxies=proxies, timeout=5)
                # 下载正常
                if req.status_code == 200:
                    break
            except:
                ip = random.choice(ip_pool)
                proxies = {'http': 'HTTP://{}'.format(ip)}
                print('章节下载超时,更换代理,重新下载。')
        # req.encoding = 'utf8'  # 错误的方式,控制台依然显示乱码
        # 乱码解决方式一:
        # req.encoding = 'gbk'
        # 乱码解决方式二:
        try:
            html = req.content.decode('gbk')
        except:
            print('gbk 解码出错')
        e = etree.HTML(html)
        # 拿到章节标题
        title = e.xpath('string(//h1)')
        # 拿到章节内容
        content = e.xpath('//div[@id="content"]/text()')
        text = ''
        for i in range(len(content)):
            text += content[i].replace('\xa0', '').replace('\r', '\n')
        # 章节名称和正文部分分行显示
        return '\n' + title + '\n\n' + text

    def run(self):
        global current_progress
        while not self.urls_queue.empty():
            num, url = self.urls_queue.get()
            text = self.download_one_chapter(url)
            current_progress += 1
            print('当前进度:', current_progress)
            '3:重写线程,构造run(), 从 urls_queue 中 get, 处理好之后将(章节号 + 章节内容)  put 到 wait_queue'
            self.wait_queue.put((num, text))
        pass


def main(aurl: str) -> None:  # 下载一本小说
    urls_queue = Queue()
    wait_queue = Queue()
    creat_ip_pool()  # 构造了IP代理池
    # print(random.choice(ip_pool))
    # 所有需要下载的url 进入队列
    all_urls_enter_queue(urls_queue=urls_queue, aurl=aurl)
    dt = []
    for i in range(100):
        t2 = DownloadNovel(urls_queue, wait_queue)
        t2.start()
        dt.append(t2)
    for i in range(100):
        dt[i].join()
    print('小说的章节遍历完毕')
    # 此时 wait_queue 中的章节为乱序存放
    all_text_lt = []
    while not wait_queue.empty():
        all_text_lt.append(wait_queue.get())
    # 排序
    all_text_lt = sorted(all_text_lt, key=lambda x: x[0])  # [(0, '...'), (1, '...'), ...]
    # 保存小说,以列表的形式写入到文件中
    text = ''
    # 愣是 忘记些 range 了
    for i in range(len(all_text_lt)):
        text += str(all_text_lt[i][1])
    with open('{}.txt'.format(novel_name), 'w') as stream:
        stream.write(text)
    print('{} 下载完成'.format(novel_name))


if __name__ == "__main__":
    url = '/1_1680'
    main(aurl=url)

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值