美文网首页
如何爬取系列博客内容到本地

如何爬取系列博客内容到本地

作者: 王灵 | 来源:发表于2020-12-15 17:44 被阅读0次

    前言

    案发经过:最近在学习python,对爬虫非常赶兴趣。在闲鱼上翻到有一本叫作《Python3网络爬虫开发实战》的书,但是又不想掏钱买,所以就在网上搜索pdf版。无意间找到了一篇博客。那么问题来了,我不想因为断网或者博客关闭什么的原因影响我,所以我要把它保存下来

    思路

    博客是这样的,里面有各个章节的目录,点击进入章节内容


    20201215164204.jpg

    我需要做什么:

    • 保存目录页为本地html文件
    • 爬取章节地址,并保存内容到本地html
    • 替换目录html里的章节地址为本地地址
    • 下载html里的img文件到本地
    • 替换html里的网络图片为本地地址

    实现

    保存目录页为本地html文件

    (本地文件名、html)

    def get_path(url):
        """
        获取本地的文件地址
        新建文件夹 htmls 用于保存html文件
        观察url地址https://cuiqingcai.com/5052.html
        决定使用crawler_5052 来命名文件
        :param url: 页面的网络地址
        :return: 本地html的地址
        """
        pattern = re.compile('https://cuiqingcai.com/(\d+).html')
        for p in pattern.finditer(url):
            return 'htmls/crawler_' + str(p.group(1)) + ".html"
    

    通过bs4的BeautifulSoup获取soup对象,方便查找筛选我们需要的内容

    import requests
    from bs4 import BeautifulSoup
    
    
    def request(url, encoding='utf-8'):
        """请求http返回soup对象"""
        headers = {'Referer': url,
                   'User-Agent': """Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Mobile Safari/537.36""",
                   }
        response = requests.get(url=url, headers=headers)
        html = response.content.decode(encoding, "ignore")
        return BeautifulSoup(html, 'html.parser')
    

    筛选获取我们需要的内容valuable_content = soup.select('.post-body')[0]
    由于是截取的html片段,但是我们需要在本地使用所以需要添加必要的html元素

    def get_html(div):
        """获取能够展示的html
        soup.select('.post-body')
        """
        return """<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    %s
    </body>
    </html>""" % (div)
    

    本地地址和html文本都获取了,接下来就是写入了

    def write_file(file_name, str):
        """
        把html文本写入本地文件
        :param file_name: 文件路径
        :param str: html文本
        :return:
        """
        # 打开文件
        fo = open(file_name, "w+")
        print("文件名: ", fo.name)
        fo.write(str)
        # 关闭文件
        fo.close()
    

    通过save_page把它们串连起来

    def save_page(url):
        """
        保存页面到本地
        :param url: 网页地址
        :return:
        """
        soup = request(url)
        # 获取真正需要到内容
        valuable_content = soup.select('.post-body')[0]
        html = get_html(valuable_content)
        # 生成一个文件名 crawler
        name = get_path(url)
        write_file(name, html)
    
    爬取章节地址,并保存内容到本地html

    读取刚刚保存的目录html文件,从里面筛选出章节地址

    def save_pages():
        """
        保存章节内容
        :return: 
        """
        # 获取本地html文件的soup对象
        soup = BeautifulSoup(open('htmls/crawler_5052.html', 'r', encoding='utf-8'), 'html.parser')
        for i in soup.select('ul li a'):# 筛选出章节的地址
            # 内容有一个错误,
            child = i.attrs['href'].replace('hhttps', 'https')
            # 保存url到本地
            save_page(child)
    
    替换目录html里的章节地址为本地地址
    def replace_paths():
        """
        替换目录html里的章节地址为本地文件
        :return:
        """
        # 获取本地html文件的soup对象
        soup = BeautifulSoup(open('htmls/crawler_5052.html', 'r', encoding='utf-8'), 'html.parser')
        html = str(soup)
        for i in soup.select('ul li a'):  # 筛选出章节的地址
            # 内容有一个错误,
            child = i.attrs['href'].replace('hhttps', 'https')
            # 获取本地路径  需要点击打开,所以要使用相对路径
            path = os.path.join('..', get_path(child))
            # 替换
            html = html.replace(child, path)
        # 重新写入
        write_file('htmls/crawler_5052.html', html)
    
    下载html里的img文件到本地

    这里只实现下载单个图片
    获取图片的本地路径

    def get_img_path(url):
        """
        根据图片地址获取本地名称
        :param url:
        :return:
        """
        # 有一些筛选出来的img里不是网络图片
        if not (str(url).startswith('https') | str(url).startswith('http')):
            return None
    
        # 获取host后面的字段
        pattern = re.compile('[.*][com/|.cn/](.*)')
        for p in pattern.finditer(url):
            f = p.group(1)
            if f.endswith('.png') | f.endswith('.jpg') | f.endswith('.ico'):
                f = f.replace('/', '_').replace('-', '_')
            else:  # 'http://epub.ituring.com.cn/api/storage/getbykey/screenshow?key=1708002eb50fe93aef25'
                f = f.split('key=')[1] + '.jpg'
            return os.path.join('image', f)
    
    def download(url, path=None):
        """
        下载图片到本地
        :param url:图片网络地址
        :param path:图片本地地址
        :return:
        """
        if not path:
            path = get_img_path(url)
        if not path:
            return
            # 判断这个文件是否存在了
        try:
            os.path.exists(path)
        except:
            print("")
        if os.path.exists(path):
            return
            # filepath = os.path.join(url, path)
        # urlretrieve(url, path)
        print('path: ' + path)
        file_data = requests.get(url, allow_redirects=True).content
        with open(path, 'wb') as handler:
            handler.write(file_data)
    
    替换html里的网络图片为本地地址
    def replace_imgs():
        """
        查找文件中的图片 下载  替换
        :return: 
        """
        # 获取文件夹下所有的文件
        for f in os.listdir('htmls'):
            replace_img(os.path.join('htmls', f))
    
    def replace_img(f):
        """
        :param f: 
        :return: 
        """
        soup = BeautifulSoup(open(f, 'r+', encoding='utf-8'), 'html.parser')
        html = str(soup)
        for i in soup.select('img'):
            image_url = i.attrs['src']
            image_name = get_img_path(image_url)
            # 获取不到本地名称就不处理
            if image_name:
                # 下载到本地
                download(image_url, image_name)
                # 替换   ../image/wp_content_uploads_2019_10_865x90.png
                # print(os.path.join('../image', os.path.basename(image_name)))
                html = html.replace(image_url, os.path.join('../image', os.path.basename(image_name)))
        # 重新写入
        write_file(f, html)
    

    整个文件

    from utils import request
    from bs4 import BeautifulSoup
    import re
    import os
    import requests
    
    # 先获取一页的内容看看
    url = "https://cuiqingcai.com/5052.html"
    
    
    def get_html(div):
        """获取能够展示的html
        soup.select('.post-body')
        """
        return """<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    %s
    </body>
    </html>""" % (div)
    
    
    def get_path(url):
        """
        获取本地的文件地址
        新建文件夹 htmls 用于保存html文件
        观察url地址https://cuiqingcai.com/5052.html
        决定使用crawler_5052 来命名文件
        :param url: 页面的网络地址
        :return: 本地html的地址
        """
        pattern = re.compile('https://cuiqingcai.com/(\d+).html')
        for p in pattern.finditer(url):
            return 'htmls/crawler_' + str(p.group(1)) + ".html"
    
    
    def write_file(file_name, str):
        """
        把html文本写入本地文件
        :param file_name: 文件路径
        :param str: html文本
        :return:
        """
        # 打开文件
        fo = open(file_name, "w+")
        print("文件名: ", fo.name)
        fo.write(str)
        # 关闭文件
        fo.close()
    
    
    def save_page(url):
        """
        保存页面到本地
        :param url: 网页地址
        :return:
        """
        soup = request(url)
        # 获取真正需要到内容
        valuable_content = soup.select('.post-body')[0]
        html = get_html(valuable_content)
        # 生成一个文件名 crawler
        name = get_path(url)
        write_file(name, html)
    
    
    def save_pages():
        """
        保存章节内容
        :return:
        """
        # 获取本地html文件的soup对象
        soup = BeautifulSoup(open('htmls/crawler_5052.html', 'r', encoding='utf-8'), 'html.parser')
        for i in soup.select('ul li a'):  # 筛选出章节的地址
            # 内容有一个错误,
            child = i.attrs['href'].replace('hhttps', 'https')
            # 保存url到本地
            save_page(child)
    
    
    def replace_img(f):
        """
        :param f:
        :return:
        """
        soup = BeautifulSoup(open(f, 'r+', encoding='utf-8'), 'html.parser')
        html = str(soup)
        for i in soup.select('img'):
            image_url = i.attrs['src']
            image_name = get_img_path(image_url)
            # 获取不到本地名称就不处理
            if image_name:
                # 下载到本地
                download(image_url, image_name)
                # 替换   ../image/wp_content_uploads_2019_10_865x90.png
                # print(os.path.join('../image', os.path.basename(image_name)))
                html = html.replace(image_url, os.path.join('../image', os.path.basename(image_name)))
        # 重新写入
        write_file(f, html)
    
    
    def download(url, path=None):
        """
        下载图片到本地
        :param url:图片网络地址
        :param path:图片本地地址
        :return:
        """
        if not path:
            path = get_img_path(url)
        if not path:
            return
            # 判断这个文件是否存在了
        try:
            os.path.exists(path)
        except:
            print("")
        if os.path.exists(path):
            return
            # filepath = os.path.join(url, path)
        # urlretrieve(url, path)
        print('path: ' + path)
        file_data = requests.get(url, allow_redirects=True).content
        with open(path, 'wb') as handler:
            handler.write(file_data)
    
    
    def get_img_path(url):
        """
        根据图片地址获取本地名称
        :param url:
        :return:
        """
        # 有一些筛选出来的img里不是网络图片
        if not (str(url).startswith('https') | str(url).startswith('http')):
            return None
    
        # 获取host后面的字段
        pattern = re.compile('[.*][com/|.cn/](.*)')
        for p in pattern.finditer(url):
            f = p.group(1)
            if f.endswith('.png') | f.endswith('.jpg') | f.endswith('.ico'):
                f = f.replace('/', '_').replace('-', '_')
            else:  # 'http://epub.ituring.com.cn/api/storage/getbykey/screenshow?key=1708002eb50fe93aef25'
                f = f.split('key=')[1] + '.jpg'
            return os.path.join('image', f)
    
    
    def replace_imgs():
        """
        查找文件中的图片 下载  替换
        :return:
        """
        # 获取文件夹下所有的文件
        for f in os.listdir('htmls'):
            replace_img(os.path.join('htmls', f))
    
    
    def replace_paths():
        """
        替换目录html里的章节地址为本地文件
        :return:
        """
        # 获取本地html文件的soup对象
        soup = BeautifulSoup(open('htmls/crawler_5052.html', 'r', encoding='utf-8'), 'html.parser')
        html = str(soup)
        for i in soup.select('ul li a'):  # 筛选出章节的地址
            # 内容有一个错误,
            child = i.attrs['href'].replace('hhttps', 'https')
            # 获取本地路径  需要点击打开,所以要使用相对路径
            path = os.path.join('..', get_path(child))
            # 替换
            html = html.replace(child, path)
        # 重新写入
        write_file('htmls/crawler_5052.html', html)
    
    
    if __name__ == '__main__':
        # save_page(url)
        # save_pages()
        # replace_paths()
        replace_imgs()
    
    

    相关文章

      网友评论

          本文标题:如何爬取系列博客内容到本地

          本文链接:https://www.haomeiwen.com/subject/nmpwgktx.html