软件测试工程师 顺序查找 https ssl colors Ractivejs vue学习 jquery循环遍历 当前线程等待5秒 cmd查看mysql版本 coreldraw入门学习 js数组截取前5个 cmd清空命令 oracle分析函数 安装mysql python中index的用法 python创建对象 python编程题 java覆盖 java的包 java正则表达式用法 java初级教程 java接口怎么写 java运算 java基础编程 javac javaenum java学习流程 javalist数组 怎么安装linux python 教程 js绝对值 pr缩放 stretchcolumns emit python图片处理 文件压缩工具 汪文君 强制删除桌面ie图标 fleaphp
当前位置: 首页 > 学习教程  > python

Python+bs4实现爬取小说并下载到本地

2021/2/6 22:30:37 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

Pythonbs4实现爬取小说并下载到本地 前言 在公司闲的无聊之际,想研究研究python的bs模块,试着写一写爬虫。但是公司有限制,娱乐网址一律不能访问,最后发现小说网站还能进,那就你了。开整~ 以前觉得这东西挺low的&…

Python+bs4实现爬取小说并下载到本地


前言

在公司闲的无聊之际,想研究研究python的bs模块,试着写一写爬虫。但是公司有限制,娱乐网址一律不能访问,最后发现小说网站还能进,那就你了。开整~

以前觉得这东西挺low的,从页面上抓取数据什么的我一直都觉得没啥意思,不过今天我居然开始感觉到了一些成就感。

一、引包

本次爬虫主要用到了两个库:

import requests
from bs4 import BeautifulSoup

requests模块用于模拟请求,获取响应页面;bs4模块用于解析响应的页面,方便获取页面标签。

二、代理问题

本来想先试试水,用简单的代码试试能不能访问页面,结果一试就出现了下面的问题:

Traceback (most recent call last):
  File "D:/PycharmProjects/NovelCrawling/novel_crawling.py", line 109, in pre_op
    book_info = search_by_kewords(keword)
  File "D:/PycharmProjects/NovelCrawling/novel_crawling.py", line 89, in search_by_kewords
    soup = BeautifulSoup(result_html, 'lxml')
  File "D:\python\lib\site-packages\bs4\__init__.py", line 310, in __init__
    elif len(markup) <= 256 and (
TypeError: object of type 'NoneType' has no len()
HTTPSConnectionPool(host='www.13800100.com', port=443): Max retries exceeded with url: /index.php?s=index/search&name=%E6%96%97%E7%BD%97%E5%A4%A7%E9%99%86&page=1 (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x000002029189FD30>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))

好奇怪的嘞,浏览器能正常访问,而且在公司但也设置了代理,怎么就请求不到呢。带着这个疑问就是一番百度,最多的结果是像这样:

response = requests.get(url, verify=False)

带上verify=False参数,说什么关闭openSSL验证啥的,咱也不懂啊,只能验证了一下,很遗憾,未果。
还有人说很多情况都是地址拼错了的问题,但我仔细检查了也没有。

难受,百度了半个小时也没能找到自己想要的答案,都想着算了,无聊就无聊吧,玩儿手机也挺香的。但是转念一想,是不是requests模块要单独设代理呢?

带着这个疑问再次进行了百度,requests模块还真能设置代理,感觉有了希望,最后成功解决!代码如下:

proxies = {
    'http': 'http://xxx.xxx.xxx.xxx:xx', # http代理
    'https': 'http://xxx.xxx.xxx.xxx:xx' # https代理
}
response = requests.get(url, verify = False, proxies = proxies)

这里还get到一个点,上面的配置意思是说,当请求是http的时候走http的代理,是https请求的时候走https的代理,但是并不是意味着https代理一定要是https的地址。 就像我这里两个代理都是设置的http代理地址。有一点点绕,其实简单来说,就是当请求是https的时候也走我设置的http代理。

当然你自己在家使用就没那么多蛋疼的问题了。

三、爬取过程

本次爬取的小说网站是138看书网https://www.13800100.com,分析阶段就不在这记录了,主要就是分析网页定位需要信息的元素问题。这里主要记录一下大概的思路:

其实感觉还是挺简单的,总的来说就三步:

1、获取章节列表,分析每一章节的下载网页
2、分析下载网页,获取每一章节的小说内容
3、将小说内容存储到文本文件中

1、获取章节列表
分析小说目录章节网页,大概长这样子:
在这里插入图片描述

通过F12开发工具分析出章节的元素位置,我们的目的是要分析出每一章节的阅读地址。这里可以根据css定位然后进行分析:

# 从网页内容中提取章节链接
def get_download_page_urls(htmlContent):
    # 实例化soup对象, 便于处理
    soup = BeautifulSoup(htmlContent, 'lxml')
    # 获取所有章节的a标签
    li_as = soup.select('.bd>ul>.cont-li>a')
    # 小说名称
    text_name = soup.select('.cate-tit>h2')[0].text
    # 下载地址
    dowload_urls = []
    for a in li_as:
        dowload_urls.append(f"{base_url}{a['href']}")
    return [text_name, dowload_urls]

我这里获取了小说的名称以及各章节的链接。


2、获取各章节的小说内容
各个章节的链接已经拿到了,接下来只需要分析阅读网页的内容并存储到文件中就可以了。网页长这样:
在这里插入图片描述
代码处理:

# 分章节下载
def download_by_chapter(article_name, url ,index):
	'''
	article:小说名称
	url:章节阅读链接
	index:章节序号
	'''
    content = get_content(url)
    soup = BeautifulSoup(content, 'lxml')
    # 章节标题
    title = soup.select('.chapter-page h1')[0].text
    # 作者部分处理
    author = soup.select('.chapter-page .author')[0].text.replace('\n', '')
    # 小说内容部分处理
    txt = soup.select('.chapter-page .note')[0].prettify().replace('<br/>', '')\
        .replace('\n', '')
    txt = txt[txt.find('>') + 1:].rstrip('</div>').replace('   ', '\n').strip()
    txt_file = open(fr"{article_name}\{'%04d' % index}_{title}.txt", mode='w',encoding='utf-8')
    txt_file.write(f'{title}\n\n{author}\n\n{txt}'.replace(' ', ' '))
    txt_file.flush()
    txt_file.close()

关于小说内容部分的处理,本来可以采用.text只获取文本内容,然后进行处理就可以了,但是操作后发现不太好分段落,还会有很多奇奇怪怪的符号。几经折腾最后还是选择了使用prettify()方法,将元素格式化成html字符串,然后进行相应的处理。
这里学到一点,关于数字的格式化:

'%04d' % index # 表示将index格式化成四位

有时候我们不想分文件下载,于是我加了一个下载到同一个文件中的方法:

# 下载到一个文件中
def download_one_book(txt_file, url, index):
	'''
	txt_file:存储文本对象
	url:章节阅读链接
	index:章节序号
	'''
    content = get_content(url)
    soup = BeautifulSoup(content, 'lxml')
    # 章节标题
    title = soup.select('.chapter-page h1')[0].text
    # 作者部分处理
    author = soup.select('.chapter-page .author')[0].text.replace('\n', '')
    # 小说内容部分处理
    txt = soup.select('.chapter-page .note')[0].prettify().replace('<br/>', '') \
        .replace('\n', '')
    txt = txt[txt.find('>') + 1:].rstrip('</div>').replace('   ', '\n').strip()
    txt_file.write(f'{title}\n\n'.replace(' ', ' '))
    # 只在第一章节写入作者
    if index == 0:
        txt_file.write(f'{author}\n\n'.replace(' ', ' '))
    txt_file.write(f'{txt}\n\n'.replace(' ', ' '))
    txt_file.flush()

四、代码扩展

很明显,程序有些僵硬,使用者必须要手动去138读书网找到小说的目录地址,才能通过本程序进行下载。所以我分析网页后,写出一个网站搜索小说的方法:

# 关键字查找书籍
def search_by_kewords(keyword, page=1):
	'''
	keyword:搜索关键字
	page:分页页号
	'''
    book_info = {}
    while True:
        search_url = f'{base_url}/index.php?s=index/search&name={keyword}&page={page}'
        result_html = get_content(search_url)
        soup = BeautifulSoup(result_html, 'lxml')
        books_a = soup.select('.s-b-list .secd-rank-list dd>a')
        for index, book_a in enumerate(books_a):
            book_info[f'{index + 1}'] = [book_a.text.replace('\n', '').replace('\t', ''), f"{base_url}{book_a['href']}".replace('book', 'list')]
        if len(books_a) == 0:
            break
        page += 1
    print(f'共查找到{page}页,{len(book_info.keys())}本书籍。')
    print('--------------------------')
    return book_info

然后做了一下细节的处理,得出了完整的程序:

import os
import sys
import time
import traceback
import warnings
import requests
from bs4 import BeautifulSoup

# 忽略警告
warnings.filterwarnings('ignore')
# 代理配置
proxies = {
    'http': 'http://xxx.xxx.xxx.xxx:xx', # http代理
    'https': 'http://xxx.xxx.xxx.xxx:xx' # https代理
}
# 138看书网地址
base_url = 'https://www.13800100.com'


# 获取目录网址内容
def get_content(url,):
    response = ''
    try:
        # user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36"
        response = requests.get(url, verify = False, proxies = proxies) # 需要使用代理的情况
        # response = requests.get(url, verify=False) # 不用代理的情况
        response.raise_for_status() # 如果返回的状态码不是200, 则抛出异常;
        return response.content.decode(encoding=response.encoding) # 解码后的网页内容
    except requests.exceptions.ConnectionError as ex:
        print(ex)


# 从网页内容中提取章节链接
def get_download_page_urls(htmlContent):
    # 实例化soup对象, 便于处理
    soup = BeautifulSoup(htmlContent, 'lxml')
    li_as = soup.select('.bd>ul>.cont-li>a')
    text_name = soup.select('.cate-tit>h2')[0].text
    dowload_urls = []
    for a in li_as:
        dowload_urls.append(f"{base_url}{a['href']}")
    return [text_name, dowload_urls]


# 分章节下载
def download_by_chapter(article_name, url ,index):
    content = get_content(url)
    soup = BeautifulSoup(content, 'lxml')
    # 章节标题
    title = soup.select('.chapter-page h1')[0].text
    # 作者部分处理
    author = soup.select('.chapter-page .author')[0].text.replace('\n', '')
    # 小说内容部分处理
    txt = soup.select('.chapter-page .note')[0].prettify().replace('<br/>', '')\
        .replace('\n', '')
    txt = txt[txt.find('>') + 1:].rstrip('</div>').replace('   ', '\n').strip()
    txt_file = open(fr"{article_name}\{'%04d' % index}_{title}.txt", mode='w',encoding='utf-8')
    txt_file.write(f'{title}\n\n{author}\n\n{txt}'.replace(' ', ' '))
    txt_file.flush()
    txt_file.close()


# 下载到一个文件中
def download_one_book(txt_file, url, index):
    content = get_content(url)
    soup = BeautifulSoup(content, 'lxml')
    # 章节标题
    title = soup.select('.chapter-page h1')[0].text
    # 作者部分处理
    author = soup.select('.chapter-page .author')[0].text.replace('\n', '')
    # 小说内容部分处理
    txt = soup.select('.chapter-page .note')[0].prettify().replace('<br/>', '') \
        .replace('\n', '')
    txt = txt[txt.find('>') + 1:].rstrip('</div>').replace('   ', '\n').strip()
    txt_file.write(f'{title}\n\n'.replace(' ', ' '))
    # 只在第一章节写入作者
    if index == 0:
        txt_file.write(f'{author}\n\n'.replace(' ', ' '))
    txt_file.write(f'{txt}\n\n'.replace(' ', ' '))
    txt_file.flush()


# 关键字查找书籍
def search_by_kewords(keyword, page=1):
    book_info = {}
    while True:
        search_url = f'{base_url}/index.php?s=index/search&name={keyword}&page={page}'
        result_html = get_content(search_url)
        soup = BeautifulSoup(result_html, 'lxml')
        books_a = soup.select('.s-b-list .secd-rank-list dd>a')
        for index, book_a in enumerate(books_a):
            book_info[f'{index + 1}'] = [book_a.text.replace('\n', '').replace('\t', ''), f"{base_url}{book_a['href']}".replace('book', 'list')]
        if len(books_a) == 0:
            break
        page += 1
    print(f'共查找到{page}页,{len(book_info.keys())}本书籍。')
    print('--------------------------')
    return book_info


# 主程序处理
def pre_op():
    start = time.perf_counter()
    try:
        print(f'本程序适用于下载138看书网小说,138看书网: {base_url}')
        print('请输入关键字查找书籍:')
        keword = input()
        print('正在查找...')
        book_info = search_by_kewords(keword)
        print('请选择对应序号进行下载:')
        print('**********************')
        for index in book_info.keys():
            print(f"{index}: {book_info[index][0]}")
        print('**********************')
        c = input('请选择:')
        result_html = get_content(book_info[c][1])
        dowload_urls = get_download_page_urls(result_html)
        print('下载链接获取完毕!')
        print('----------------------')
        print('请选择下载模式:')
        print('1.分章节下载')
        print('2.整本下载')
        c = input()
        txt_file = ''
        if not os.path.exists(fr'./{dowload_urls[0]}'):
            os.mkdir(fr'./{dowload_urls[0]}')
        if c == '2':
            txt_file = open(fr"{dowload_urls[0]}\{dowload_urls[0]}_book.txt", mode='a+', encoding='utf-8')
        for index, dowload_url in enumerate(dowload_urls[1]):
            if c == '1':
                download_by_chapter(dowload_urls[0], dowload_url, index + 1)
            else:
                txt_file.write(f'{dowload_urls[0]}\n\n')
                download_one_book(txt_file, dowload_url, index)
            sys.stdout.write('%\r')
            percent = str(round(float(index + 1) * 100 / float(len(dowload_urls[1])), 2))
            sys.stdout.write(f'正在下载...{percent} %')
            sys.stdout.flush()
        txt_file.close()
        print(f'\n下载完毕!共计{len(dowload_urls[1])}章节.耗时:{str(round(time.perf_counter() - start, 2))}s.')
        print('=======================================================')
    except:
        traceback.print_exc()


if __name__ == '__main__':
    pre_op()

看一下最终的效果:

本程序适用于下载138看书网小说,138看书网: https://www.13800100.com
请输入关键字查找书籍:
斗罗大陆
正在查找...
共查找到4页,20本书籍。
--------------------------
请选择对应序号进行下载:
**********************
1: 斗罗大陆之冰凰斗罗
2: 斗罗大陆之我本蓝颜
3: 斗罗大陆之剑决天下
4: 斗罗大陆之极限
5: 斗罗大陆III龙王传说(龙王传说)
6: 斗罗大陆之青莲剑帝姬
7: 斗罗大陆3龙王传说
8: 斗罗大陆之神圣龙斗罗
9: 斗罗大陆之昊天传说
10: 斗罗大陆之白凤传奇
11: 斗罗大陆之红颜系统
12: 斗罗大陆之仙神纪
13: 斗罗大陆之时崎狂三
14: 斗罗大陆lll龙之御尘
15: 斗罗大陆之焰门传奇
16: 斗罗大陆国服达摩玉小刚
17: 斗罗大陆的魔法师
18: 斗罗大陆一剑倾世
19: 斗罗大陆之灵魂手笔
20: 斗罗大陆之倾尽天下
**********************
请选择:11
下载链接获取完毕!
----------------------
请选择下载模式:
1.分章节下载
2.整本下载
2
正在下载...100.0 %
下载完毕!共计153章节.耗时:220.17s.
=======================================================

在这里插入图片描述
至此,就是今日学习的成果了,本来想克服一下使用多线程提高一下下载速度,但是本人对多线程实在是很薄弱,折腾了也没弄出来。如果有谁能帮我改成多线程,感激不尽。

最后,欢迎大家留言一起探讨!有什么不合适的地方请指正。


本文链接: http://www.dtmao.cc/news_show_2000137.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?