吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6847|回复: 11
上一主题 下一主题
收起左侧

[Python 转载] 【萌新向】在线小说正本抓取+内容简单排版【塔读文学】

[复制链接]
跳转到指定楼层
楼主
roshinntou 发表于 2021-4-8 13:16 回帖奖励
这次主要跟大家分享一下python爬虫使用过程中可能会遇到的一些小问题和爬取的简单思路。

我本身也不是程序员出身,python相关内容也都是自学,如果各位大佬发现我的代码里有可以优化、改进的地方,还请不吝赐教。即便是跟我一样的普通萌新,如果发现了更好的解决方法也请分享出来,大家一起进步。

任务目标:
        从塔读文学的网站里抓取小说,并且保存为TXT格式。
        简单级应用,输入小说目录网址,抓取全部的内容。

程序流程:
    1、抓取小说目录网页信息
    2、获取全部章节的名称和链接
    3、依次进入每一章的页面,抓取内容并按照顺序写入同一个txt文件里

对萌新来说使用到的技术:
1、urllib.request库  -----操作url
2、循环与break、continue
3、time库 -------系统暂停
4、lxml库 --------  获取html代码里的指定内容
5、string.replace()方法 --------  修改替换字符串内容
6、with open() 方法操作文件

都是一些简单的技术,思路也比较简单,给大家做一个参考。

首先是章节内容,通过DevTools可以发现文章内容在一个div里,整个html代码有相关注释


那么复制xpath之后,任务就变成了获取   【 //*[@id="partContent"] 】下面的所有P标签的内容形成一整个章节内容。

我们先从简单的开始做,获取网页内容,然后找到所有章节文字打印出来看看效果。

[Python] 纯文本查看 复制代码
# -*- coding: utf-8 -*-
"""
Created on Wed Apr  7 15:32:39 2021

@author: roshinntou
"""

from lxml import etree
import urllib.request
import time
mainUrl = 'http://www.tadu.com'

def get_html_code(url):
    max_count = 6
    htmlcode = ''
    for i in range(max_count):
        print('No.'+str(i)+'   start load:' + url)
        try:
            req_one = urllib.request.Request(url)
            req_one.add_header('User-Agent', 'Mozilla/6.0')
            res_one = urllib.request.urlopen(req_one, timeout=60)
            htmlcode = res_one.read().decode('utf-8')
            res_one.close()
            break
        except:
            if i < max_count:
                continue
            else:
                print('多次重复读取网页信息失败')
    time.sleep(1)
    return htmlcode


if __name__ == '__main__':
    code = get_html_code('http://www.tadu.com/book/725327/86240835/')
    html = etree.HTML(code)
    text = html.xpath('//*[@id="partContent"]/p')
    for i in text:
        print(i.text)



这里urllib.request获取html代码的过程单提取出了一个方法,我把核心代码放进for循环中,并加入 try/catch模块,如果一切正常就break结束当前循环,如果报错就判断重复了多少次,在次数规定内的话,使用continue重复执行核心代码,如果超出规定的次数则提示错误。(友情提示:爬取网页代码经常会因为网络波动或者其他原因超时报错。不加入循环方法的话要么爬虫程序停止,要么这一部分的内容没有爬取到,会影响最后的结果。)

然后是内容的处理,通过xpath 我们获取到了 partContent div下的所有P标签,然后通过for循环打印每一个p标签的text文本。


但是执行结果是:

读取html代码的模块并没有报错重复执行,只执行了一次就结束了。整个结果没有报错,证明至少所有步骤都执行下来了包括for循环也没有出错,没有超出下标之类的错误的同时有没有任何结果,应该时for循环执行了0次,P标签没有获取到。


那么我们直接打印获取出来的html代码看看是什么情况
结果是:


可以看到,DIV都找到了,但是里边(红框位置)没有文章内容。所以刚刚执行没有报错,也没有结果。

那么这里应该是之后动态加载的。

重新刷新页面,看看network内部的内容


然后发现这个script里有我们需要的东西


乱码是需要utf-8格式重新编码,callback 是js的回调,那么网页代码里肯定有相关链接。我们打开网页源代码搜索一下“1462b”这个开头的东西可以看到:


可以看到这个input 的 ID就叫 bookPartResourceUrl ,value里的内容就是刚刚的链接了。那么我们现在获取一下文章页面里这个链接的url地址,然后再次获取一下这个url地址的结果看看。主方法的代码是:

[Python] 纯文本查看 复制代码
if __name__ == '__main__':
    code = get_html_code('http://www.tadu.com/book/725327/86240835/')
    html = etree.HTML(code)
    input_info = html.xpath('//*[@id="bookPartResourceUrl"]')[0].get('value')
    code = get_html_code(input_info)
    print(code)




第一步获取这个页面的信息,然后找到这个input元素,获取它的value属性,获得的是一个url链接,然后再次读取这个链接的信息,得到的结果是:


可以看到读取了2次网页之后,获取到了文章内容。每行内容都在P标签内。

这个时候就可以使用了,使用方法有多种:
1、可以直接把包含P标签的HTML代码保存起来,然后放在网站内使用。
2、存txt的情况下,可以用 split 方法以'</p><p>'为目标把这段内容截取成数组,代码如下:
[Python] 纯文本查看 复制代码
string.split('</p><p>')

3、我是直接使用replace方法,把'</p><p>'替换成‘\n’然后前后内容固定的,直接截取一下string[22:-7]就获得了整张内容,然后就可以直接写入txt文件了。不用循环录入每一句的话,性能消耗也比较少

写入的方法也很简单:

[Python] 纯文本查看 复制代码
 with open('网游:每十小时创造一个BUG.txt','a',encoding='utf-8') as file:
        file.write('\n'+title+'\n')
        file.write(text+'\n')
        print('保存成功!')    


用with open 的 a 模式,文件名可以获取一下当作参数传进来,然后标题和内容换好行,连续写入同一个文件就行,现在主流的手机小说软件都是一个txt,多个之间也不好切换导入。别把每一章单独存一个文件。



到现在为止,我们已经能根据章节URL获取到章节的文字内容了,下一步,我们需要一次获取所有章节的标题和URL,然后依次读取所有章节内容,保存到一个txt文件里

先看一下目录结构:


也是很简单的结构,每一个章节对应一个a标签。  xpath 是//*[@id="content"]/div[3]/div[2]/a

所以我们获取章节并进一步获取文章内容的方法应该是:

[Python] 纯文本查看 复制代码
def get_all_url(url):
    code = get_html_code(url)
    print('get etree')
    html = etree.HTML(code)
    urlList = html.xpath('//*[@id="content"]/div[3]/div[2]/a')
    for i in urlList:
        title = i.text
        title = title.replace(" ","").replace("\r\n","").replace(" ","").replace("\r","").replace("\n","")
        url = i.get('href').replace(" ","").replace("\r\n","").replace(" ","").replace("\r","").replace("\n","")
        print('title:'+title)
        print('url:'+mainUrl+url)
        get_text_by_url(mainUrl+url,title)


因为章节名称前后有空格和换行,我就给他们全部替换了一下。获取纯文本信息。
for循环是将每一章的标题和URL传给刚刚我们做好的方法里,保存内容用的。


最后:

完整的代码是:
[Python] 纯文本查看 复制代码
# -*- coding: utf-8 -*-
"""
Created on Thu Apr  8 11:48:39 2021

@author: roshinntou
"""

from lxml import etree
import urllib.request
import time
mainUrl = 'http://www.tadu.com'

def get_html_code(url):
    max_count = 6
    htmlcode = ''
    for i in range(max_count):
        print('No.'+str(i)+'   start load:' + url)
        try:
            req_one = urllib.request.Request(url)
            req_one.add_header('User-Agent', 'Mozilla/6.0')
            res_one = urllib.request.urlopen(req_one, timeout=60)
            htmlcode = res_one.read().decode('utf-8')
            res_one.close()
            break
        except:
            if i < max_count:
                continue
            else:
                print('多次重复读取网页信息失败')
    time.sleep(1)
    return htmlcode

def get_all_url(url):
    code = get_html_code(url)
    print('get etree')
    html = etree.HTML(code)
    urlList = html.xpath('//*[@id="content"]/div[3]/div[2]/a')
    for i in urlList:
        title = i.text
        title = title.replace(" ","").replace("\r\n","").replace(" ","").replace("\r","").replace("\n","")
        url = i.get('href').replace(" ","").replace("\r\n","").replace(" ","").replace("\r","").replace("\n","")
        print('title:'+title)
        print('url:'+mainUrl+url)
        get_text_by_url(mainUrl+url,title)


def get_text_by_url(url,title):
    code = get_html_code(url)
    
    html = etree.HTML(code)
    input_info = html.xpath('//*[@id="bookPartResourceUrl"]')[0].get('value')
    print(input_info)
    
    code = get_html_code(input_info)
    #text = code[22:-7].split('</p><p>')
    text = code[22:-7].replace('</p><p>', '\n')

    with open('网游:每十小时创造一个BUG.txt','a',encoding='utf-8') as file:
        file.write('\n'+title+'\n')
        file.write(text+'\n')
        print('保存成功!')    

    

if __name__ == '__main__':
    get_all_url('http://www.tadu.com/book/catalogue/743142')
    #get_text_by_url('http://www.tadu.com/book/749916/91083959/','title')



每次执行的时候复制小说的目录URL到get_all_url 方法的参数里,然后把  上边  with open里的文件名改一下就行。



到现在为止,我们就成功的通过目录URL获取到小说的全部信息了。

塔读这边抓的太快会被封。请注意。

程序还有很多可以改进的地方:
1、输入书名,自动搜索下载的功能,也比较简单,搜索出来的小说链接是总页链接
比如:http://www.tadu.com/book/725327   这个是总页链接,但是目录链接是固定的,book 和后边id编号之间添加catalogue,既就是http://www.tadu.com/book/catalogue/725327,获取链接之后可以自己拼接一下。

还有就是这个网站的搜索,url里隐藏了参数。直接搜索显示默认url:http://www.tadu.com/search


实际上还是有提交数据的



然后测试一下接口:


发现参数是http://www.tadu.com/search?query=参数

这样就可以直接查询并获取内容了。




2、多进程爬取。现在的代码下载一个一两千章的小说要挺久的。可以多进程一起爬,简单点的方法是全部独立下载然后重新拼接在一起。或者使用协程处理。
不过我这边没有http代{过}{滤}理,老是被封比较无奈,等我搞到了稳定能用的http代{过}{滤}理之后再来跟大家分享多进程协程处理爬虫的开发心得。




附上完整代码: book_crawler .zip (1.13 KB, 下载次数: 22)

免费评分

参与人数 6吾爱币 +10 热心值 +6 收起 理由
laiwankaikan + 1 谢谢@Thanks!
咸鱼一浩 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wushaominkk + 5 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
cckkopit + 1 + 1 用心讨论,共获提升!
子晗。 + 2 + 1 用心讨论,共获提升!
ccwuax + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| roshinntou 发表于 2021-4-8 15:50 |楼主
gentlespider 发表于 2021-4-8 13:23
楼主多进程/多线程/携程需要考虑一个问题就是顺序的问题。可以带一个flag过去,到时候合并也能按f ...

之前研究多线程,感觉效率没有想象中那么好,查了一下,好像是因为即使多线程,在一个进程内也只有一个gil,gil导致性能下降。

后来用多进程之后效率一下子起飞,只是多个进程之间不互通数据,只能考虑队列或者管道,队列的文档多一些,好像更容易实现的样子。不过我还没彻底掌握,还在不断探索之中。。。
推荐
 楼主| roshinntou 发表于 2021-4-8 17:25 |楼主
gentlespider 发表于 2021-4-8 16:45
获取的顺序一定会乱啊,多进程多线程携程都一样。为啥会快,因为是异步,异步获取的顺序就是不确定的,所 ...

单独把文件合并,还是加顺序标记存进内存最后整合?

我之前是加锁的方式来依次生成的,比如3条线程,a线程在a锁 解锁 后执行程序,执行完之后释放b锁,然后b线程再执行,释放C锁,以此类推。保证写入顺序的。看来我还有很多需要学习的
沙发
gentlespider 发表于 2021-4-8 13:23
楼主多进程/多线程/携程需要考虑一个问题就是顺序的问题。可以带一个flag过去,到时候合并也能按flag的顺序合并
3#
ccwuax 发表于 2021-4-8 13:27
二次爬取的思路蛮好的,学习了
4#
桃花笑 发表于 2021-4-8 13:45
大神啊,厉害了
5#
key_user 发表于 2021-4-8 14:17
收藏备用学习哈
6#
天才笨蜀黍 发表于 2021-4-8 14:51
够详细,学习了~感谢分享~~
8#
gentlespider 发表于 2021-4-8 15:57
roshinntou 发表于 2021-4-8 15:50
之前研究多线程,感觉效率没有想象中那么好,查了一下,好像是因为即使多线程,在一个进程内也只有一个gi ...

多线程在IO密集型操作程序上还是蛮快的,大多数爬虫程序都是IO密集型,没有用到CPU的,所以相对来说大多时候的爬虫程序多线程要比多进程快,但也要看实际程序需求和你计算机的硬件咋样。
9#
 楼主| roshinntou 发表于 2021-4-8 16:41 |楼主
gentlespider 发表于 2021-4-8 15:57
多线程在IO密集型操作程序上还是蛮快的,大多数爬虫程序都是IO密集型,没有用到CPU的,所以相对来说大多 ...

嗯嗯,那我再研究研究。操作IO的话只要顺序别乱应该好写很多吧。
10#
gentlespider 发表于 2021-4-8 16:45
roshinntou 发表于 2021-4-8 16:41
嗯嗯,那我再研究研究。操作IO的话只要顺序别乱应该好写很多吧。

获取的顺序一定会乱啊,多进程多线程携程都一样。为啥会快,因为是异步,异步获取的顺序就是不确定的,所以开始我说加个flag,方便爬完按顺序合并
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 16:14

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表