「玩转Python」打造十万博文爬虫篇

前言

这里以爬取博客园文章为例,仅供学习参考,某些ad满天飞的网站太浪费爬虫的感情了。

爬取

  • 使用 beautifulsoup 获取博文

  • 通过 html2text 将 html 转 markdown

  • 保存 markdown 到本地文件

  • 下载 markdown 中的图片到本地并替换图片地址

  • 写入数据库

工具

使用到的第三方类库:beautifulsoup、html2text、pooleddb

代码

获取博文:

# 获取标题和文章内容
def gethtml(blog):
    res = requests.get(blog, headers=headers)
    soup = beautifulsoup(res.text, 'html.parser')
    # 获取博客标题
    title = soup.find('h1', class_='posttitle').text
    # 去除空格等
    title = title.strip()
    # 获取博客内容
    content = soup.find('div', class_='blogpost-body')
    # 去掉博客外层的div
    content = article.decode_contents(formatter="html")
    info = {"title": title, "content": content}
    return info

html 转 markdown:

# 这里使用开源第三方库 html2text
 md = text_maker.handle(info['content'])

保存到本地文件:

def createfile(md, title):
    print('系统默认编码:{}'.format(sys.getdefaultencoding()))
    save_file = str(title) +".md"
    # print(save_file)
    print('准备写入文件:{}'.format(save_file))
    # r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
    # w+ 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
    # a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
    f = codecs.open(save_file, 'w+', 'utf-8')
    f.write(md)
    f.close()
    print('写入文件结束:{}'.format(f.name))
    return save_file

下载图片到本地并替换图片地址:

def replace_md_url(md_file):
    """
    把指定md文件中引用的图片下载到本地,并替换url
    """

    if os.path.splitext(md_file)[1] != '.md':
        print('{}不是markdown文件,不做处理。'.format(md_file))
        return

    cnt_replace = 0
    # 日期时间为目录存储图片
    dir_ts = time.strftime('%y%m', time.localtime())
    isexists = os.path.exists(dir_ts)
    # 判断结果
    if not isexists:
        os.makedirs(dir_ts)
    with open(md_file, 'r', encoding='utf-8') as f:  # 使用utf-8 编码打开
        post = f.read()
        matches = re.compile(img_patten).findall(post)
        if matches and len(matches) > 0:
            for match in list(chain(*matches)):
                if match and len(match) > 0:
                    array = match.split('/')
                    file_name = array[len(array) - 1]
                    file_name = dir_ts + "/" + file_name
                    img = requests.get(match, headers=headers)
                    f = open(file_name, 'ab')
                    f.write(img.content)
                    new_url = "https://blog.52itstyle.vip/{}".format(file_name)
                    # 更新md中的url
                    post = post.replace(match, new_url)
                    cnt_replace = cnt_replace + 1

        # 如果有内容的话,就直接覆盖写入当前的markdown文件
        if post and cnt_replace > 0:
            url = "https://blog.52itstyle.vip"
            open(md_file, 'w', encoding='utf-8').write(post)
            print('{0}的{1}个url被替换到{2}/{3}'.format(os.path.basename(md_file), cnt_replace, url, dir_ts))
        elif cnt_replace == 0:
            print('{}中没有需要替换的url'.format(os.path.basename(md_file)))

写入数据库:

# 写入数据库
def write_db(title, content, url):
    sql = "insert into blog (title, content,url) values(%(title)s, %(content)s, %(url)s);"
    param = {"title": title, "content": content, "url": url}
    mysql.insert(sql, param)

小结

互联网时代一些开放的博客社区的确方便了很多,但是也伴随着随时消失的可能性,最好就是自己备份一份到本地;你也可以选择自己喜欢的博主,爬取下收藏。

源码:https://gitee.com/52itstyle/python

演示:

列表:

详情:

相关推荐:

如何在Python中使用asyncio.shield防止关键任务被取消?

asyncio.shield必须用于关键清理操作(如日志落盘、事务提交、连接关闭),防止被取消中断导致数据不一致;应只包裹协程本身(shield(coro)),而非Task,且内部子await需单独shield。 asyncio.shield什么时候必须用? 当一个asyncio.Task正在执行关...

为什么Python单例模式在多线程环境下会失效?

__new__中判空非原子操作会导致多线程重复创建实例;DCL通过无锁初判+加锁后复判解决,需配合__init__幂等防护。 __new__中的if判断不是原子操作 线程A执行到ifcls._instanceisNone时为True,刚准备调用super().__new__(cls),此时被调度挂起...

如何修复Python aiohttp请求时出现的ClientPayloadError?

ClientPayloadError表明服务端提前关闭连接,非客户端代码错误;常见于服务端超时、拒绝大响应或代理截断,需通过抓包、检查响应头、服务端日志及添加读取超时和异常处理来定位解决。 ClientPayloadError通常意味着服务端提前关闭了连接 这个错误不是客户端代码写错了,而是aioh...

为什么在大型Python项目中推荐使用Monorepo环境管理?

Monorepo是应对跨子包频繁修改、版本对齐成本高、CI耗时爆炸等痛点的止损方案;需统一依赖管理、确保本地与CI环境一致,并严守子包独立测试边界。 Monorepo不是“更先进”,而是对特定规模和协作模式的项目更可控——当Python项目中出现跨子包频繁修改、版本对齐成本高、CI耗时爆炸或本地开发...

为什么Python爬虫在解析复杂表格时推荐使用Pandas库?

pandas.read_html能自动解析HTML表格并修复合并单元格、多级表头等结构,但需合理配置match、header、skiprows等参数,并处理JS渲染、数据类型、空值等问题,否则易导致列错位、类型错误等隐患。 因为pandas.read_html能直接把HTML表格转成DataFram...

为什么在Python项目中推荐使用Poetry管理包依赖?

poetryinstall与pipinstall-rrequirements.txt的本质差异在于:前者是“解析后装”,基于poetry.lock中已验证的完整依赖图确保版本兼容并自动激活专属虚拟环境;后者是“盲装”,仅按文本顺序安装、不解析依赖冲突、不隔离环境,易导致静默覆盖和全局污染。 因为pi...