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

2020-12-15  本文已影响0人  王灵

前言

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

思路

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


20201215164204.jpg

我需要做什么:

实现

保存目录页为本地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()

上一篇下一篇

猜你喜欢

热点阅读