小白学习日记4_Python爬虫(requests/get)方式_2020年5月24日

有好几天都没有更新了,但是也不代表博主没有在学习,只是因为去学习了更难的东西(只是博主个人认为哈),其实学下来发现也很简单,不理解就难,理解则易。

那么这几天我到底干嘛去了呢?前两天吧去顺着廖神的教程看了看 filter 什么的,觉得有点迷茫,因为都很理论化的东西并没有实践,于是博主去找了一个爬虫的教程,开始做点实践的项目来调剂调剂这无聊的学习生活。

话不多说先放上我找的学习视频,我觉得刚好适合我这个小白的难度,讲的也很通透,倒退快进的看看知识点很容易爬一个网站,现在放上视频链接,有兴趣的同学自己琢磨琢磨:http://www.iqiyi.com/w_19ry0x4d11.html

这个视频是这个老师用来宣传自己网课的,是免费的VIP试看,既然看了人家的东西就顺带这帮忙提一嘴,各自自己把握,我还是走我的白嫖路线哈哈。主要的课程在 10:40 ~ 01:27:18 这个时间段之间,前面和后面全是自己在打广告,但我说的话讲的确实好。

话不多说放上自己的爬虫代码 + 心得体会,我尽量注释全面:

import requests    #最主要的核心部分,与网站产生关联,包扩get啊exception啊什么的
import re          #其次是这个re块,提供了一个findall配合(.*?)这个符号来提取html的内容  
import os          #这个啊文件啊什么的动的块,用来将提取的内容创建文件夹啊文件啊什么的
from datetime import datetime    #这个就是个now函数用来反回错误及错误时间的
#这首先是一堆import

def get_cnn(url):   #将连接这个功能独立出来
    n = 0
    while 1:
        n = n + 1
        try:    #尝试重连并返回重连的次数
            response = requests.get(url, timeout=(3.05, 21))    #timeout分别对应 连接时间 和 读取时间
            response.encoding = 'utf-8'
            return response.text
        except requests.exceptions.RequestException:
            print('站点错误:' + url + '\n'
                  '第' + str(n) + '次:' + str(datetime.now())[:-7])   #datetime需要单独import,[:-7]是因为后面有 . + 6个浮点数不好看所以去掉

def get_nevol_list(url):   #首先取得全部书名目录]
    html = get_cnn(url)
    reg = r'<a href="(.*?)" target="_blank"><img data-original="'   #(.*?)表示不管是什么都要取出来
    return re.findall(reg, html)

def get_nevol_name(nevol_url):   #为每一本小说创建单独的文件夹
    html = get_cnn(nevol_url)
    reg = r'''<script>
window.bookid = (.*?);
window.res_type = '(.*?)';
window.res_name = '(.*?)';
</script>'''
    return re.findall(reg, html)[0][0] + re.findall(reg, html)[0][2]

def create_nevol_file(file_name):
    path = r'C:\Users\admin\Desktop\无聊文学汇编\\' + file_name
    _folder_exists = os.path.exists(path)
    if not _folder_exists:
        os.makedirs(path)
    else:
        pass

def get_chapter_list(nevol_url):
    html = get_cnn(nevol_url)
    reg = r'<a href="(.*?)" class="button read">开始阅读</a>'
    chapter_list_url = 'http://www.msxf.cn/' + re.findall(reg, html)[0]   #每本小说的链接只有一个开始阅读,要把这个取出来变为str
    html = get_cnn(chapter_list_url)
    reg = r'<a href="http://www.msxf.cn/(.*?)" title="字数:(.*?)">(.*?)</a>'    #<a href="http://www.msxf.cn/book/81047/2186761.html" title="字数:1426 发布时间:2015-10-17 20:51">1. 第一章 初入佐家</a>
    return re.findall(reg, html)

def get_chapter_content(chapter_url):
    html = get_cnn(chapter_url)
    reg = r'<div id="article-content-body">(.*?)<div id="article-content-readtip"></div>'   #当遇到()时需要改为\(\),用 \ 对这些符号进行转义
    #开始针对VIP章节做优化
    if re.findall(reg, html, re.S):     #因为所有的[]/()/""/''/0都是false,所以当什么都没有取到时就是false
        return re.findall(reg, html, re.S)[0]  #当内容有很多行的时候需要加入第三个参数 re.S 如果要保存为txt则需要多次加工[.replace('<p>  ', '').replace('</p>', '\n').replace(' ', '')]
    else:
        return '后续为vip章节-done!'

def already_check(file_name):   #这两个我自己命名的话就是断点续传低配版
    path = r'C:\Users\admin\Desktop\无聊文学汇编\\' + file_name + '\\' + '后续为vip章节-done!.txt'
    return os.path.exists(path)

def already_check_chapter(path):
    return len(list(os.walk(path))[0][2])

def illegal_str_replace(str):   #这一块替换掉文件名中无法保存的非法字符
    str = str.replace('\\', '')
    str = str.replace('/', '')
    str = str.replace('*', '')
    str = str.replace('?', '')
    str = str.replace('"', '')
    str = str.replace('>', '')
    str = str.replace(':', '')
    str = str.replace('<', '')
    str = str.replace('|', '')
    return str

list_url_all = [r'http://www.msxf.cn/shuku/0-0-0-0-0-0-0-' + str(i) + r'.html' for i in range(0, 579)]

for url in list_url_all:
    for nevol_url in get_nevol_list(url):
        file_name = get_nevol_name(nevol_url)
        path = r'C:\Users\admin\Desktop\无聊文学汇编\\' + file_name
        create_nevol_file(file_name)    #这两步都是创建小说对应的新文件夹
        #这里要创建一个检查方案,如果有了 后续为vip章节-done! 这个文件则下一个
        if not already_check(file_name):
            #这里需要进一步优化断点续传
            acc = already_check_chapter(path)
            for i in get_chapter_list(nevol_url)[acc:]:     #从acc开始继续爬
                chapter_url = 'http://www.msxf.cn/' + i[0]
                chapter_name = illegal_str_replace(i[2])
                chapter_content = get_chapter_content(chapter_url)
                if chapter_content == '后续为vip章节-done!':
                    fn = open(path + '\\' + chapter_content + '.txt', 'w')
                    break
                else:
                    try:
                        fn = open(path + '\\' + chapter_name + '.html', 'w', encoding="utf-8")   #'w'模式是写入 write 解释:https://www.runoob.com/python/python-func-open.html
                    except:
                        fn = open(path + '\\' + illegal_str_replace(chapter_name) + '.html', 'w', encoding="utf-8")     #包含不可使用的文件名比如 ?
                        print(illegal_str_replace(chapter_name))
                    fn.write(chapter_content)   #写入文件
                    fn.close()
        else:
            pass

之所以放上这个代码是因为视频说了是爬全站,但是呢,他就爬了一本书就开始打广告了,所以我修改了很多部分来真正的做到爬全站。如果你是小白也确实不知道怎么爬,我建议你先看完上面的视频,再来结合你的思想和我的思想完善这个项目。

大体思路是:

①先获取全部的书的主url
②然后获取每本书的章节url
③然后获取正文
④将正文保存到文件并创立对应的文件夹

思路很简单但是中间遇到了不少挫折,我们且按这个执行步骤慢慢道来。

第一个挫折:①和②中与网站互动拿到url这个事情很不简单。【连接超时,连接错误等导致代码中断】

针对这个问题百度啊Google啊查了很多,主要有两个知识点。

第一个知识点是 requests.get(url, timeout(3.05, 7)) 这句话,通常情况下我们只有 url 没有 timeout 的限定,为什么要有这个 timeout 的限定,是因为提高代码效率,前面的 3.05 是秒,指的是网站的连接时间,默认都是3秒为一个循环,超过3秒没有连接到基本就不要在浪费时间了不然一直连接下去代码跟死了有什么区别,第二个 7 同理,是读取时间,纵使连接上了也有可能读取内容的时候无限读取下去,那么问题来了,这和代码死了有什么区别?因此我们限定这个 timeout 就是这两个步骤超过多久没反应之后我们就报出一个错误。当然这只是第一步,这里顺利的引出第二个知识点,如何处理错误。

第二个知识点是这个 try: + except requests.exceptions.RequestException: 这句话,就是当错误类型属于这个我们定义的连接方面的错误时则这样执行,不然代码本身的问题还是正常抛出修改。同时我针对这个错误的处理改了一下,无限循环,因为可能网不好各种各样的因素导致代码被中断很长时间,这个时候若是在爬很多数据要1、2天时间那就中断了跟死了没什么区别,所以我写了个 while 1: 来无限循环因网络不畅而导致的问题,同时用 print 把错误的日志打印出来一遍了解整个过程。

 

第二个挫折:④在保存文件的时候也遇到了问题,也是两个知识点。【编码错误,文件名非法】

第一个知识点是 open(path, 'w', encoding='utf-8') 最后的这句 encoding 中,因为Windows系统打开的文件默认是 GBK 编码的,但是我们在 get html 数据的时候就规定了是 utf-8 编码,导致有时候有些字符无法转换过来,具体原因我大致看了下说的很底层我觉得这不是我们应该关注的重点,我们重点是如何解决,那就是最后这句规定打开的文件默认使用 utf-8 编码,这样就可以规避掉编码错误的问题。

第二个知识点是文件名非法,先来看一个很熟悉的图:

当我们在用章节或小说名命名的时候就很容易遇到这个问题,于是需要使用 replace 这个函数来将这个非法字符都替换掉,写的有点复杂我自己也不好意思拿出手但是没办法才疏学浅各位将就看看,如果有更好的办法也感谢回复出来让我学习学习。

但是我们也没有必要每一个文件名都这样加工,所以我想到了 try + except 来处理,如果错误了我们才加工这个名字。致此我们才能顺利的保存文件及 copy 内容。

 

第三个挫折:这个挫折是在调整挫折的时候发现的,就是我会每次都从第一个数据开始重写,虽然 open(file, 'w') 函数和 os.path.exists(path) 函数可以分别覆盖原文件或跳过已有文件夹但是还是会重复执行已经执行过的事务,于是我优化了一下这个问题,用我的理解就是低配版的【断点续传】TAT

def already_check(file_name):
这个可以检查当前文件夹是否已完成,如果完成了就下一个,不会浪费内存也不会给网站带来压力
def already_check_chapter(path):    #这个自行百度一下walk函数就好了,什么都说了就没意思,自己查一查增强记忆
这个可以检查完成了多少章节,并返回一个数值,结合下面这2行代码可以从已完成的那一章开始重新作业
acc = already_check_chapter(path)
for i in get_chapter_list(nevol_url)[acc:]:  #切片器咯

那么到目前为止这个代码都在好好的运行并且可以看到在什么时候连接超时了多少次,以上有什么可以优化或不对的地方也请各位朋友能够费神指正一下,感谢!

我现在琢磨的就是怎么利用云服务器来完成这些,下一篇的分享应该是利用与服务器运行,好了,今天的分享就到这里了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值