前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Python的BeautifulSoup库实现一个可以爬取1000条百度百科数据的爬虫

使用Python的BeautifulSoup库实现一个可以爬取1000条百度百科数据的爬虫

作者头像
端碗吹水
发布2020-09-23 14:05:39
2.3K0
发布2020-09-23 14:05:39
举报
文章被收录于专栏:程序猿的大杂烩

BeautifulSoup模块介绍和安装

BeautifulSoup安装很简单,我们可以直接使用pip来安装BeautifulSoup,安装命令如下:

pip install beautifulsoup4

如果使用的IDE是Pycharm的话,安装更简单,直接编写导入模块的语句:import bs4,然后会报错,提示模块不存在,接着按 alt + 回车,会出现错误修正提示,最后选择安装模块即可自动安装。

安装完成之后编写一段测试代码:

代码语言:javascript
复制
import bs4

print(bs4)

如果执行这段代码,并且正常输出没有报错则代表已经安装成功。

BeautifulSoup的语法:

访问节点信息:

语法格式:

代码语言:javascript
复制
from bs4 import BeautifulSoup
import re

# 根据HTML网页字符串内容创建BeautifulSoup对象
soup = BeautifulSoup(html_doc,              # HTML文档字符串
                     'html.parser',         # HTML解析器
                     from_encoding='utf-8'  # HTML文档的编码,在python3中不需要加上这个参数
                     )

# 方法:find_all(name, attrs, string)

# 查找所有标签为 a 的节点
soup.find_all('a')

# 查找所有标签为 a 的节点,并链接符合/view/123.html形式的节点
soup.find_all('a', href='/view/123.html')
soup.find_all('a', href=re.compile('/view/\d+\.html'))

# 查找所有标签为div,class为abc,标签内容为Python的节点
soup.find_all('div', class_='abc', string='标签内容为Python的节点')

# 得到节点:<a href='1.html'>Python</a>

# 获取查找到的节点的标签名称
node.name

# 获取查找到的a节点的href属性
node['href']

# 获取查找到的a节点的链接文字
node.get_text()

实际的测试代码:

代码语言:javascript
复制
from bs4 import BeautifulSoup
import re

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

# 创建BeautifulSoup对象
soup = BeautifulSoup(html_doc, 'html.parser')

print("获取所有的连接")
links = soup.find_all('a')
for link in links:
    print(link.name, link['href'], link.get_text())

print("\n获取lacie的连接")
link_node = soup.find('a', href='http://example.com/lacie')
print(link_node.name, link_node['href'], link_node.get_text())

print("\n使用正则表达式进行匹配")
link_node = soup.find('a', href=re.compile(r"ill"))
print(link_node.name, link_node['href'], link_node.get_text())

print("\n获取p段落文字")
p_node = soup.find('p', class_="title")
print(p_node.name, p_node.get_text())

实例爬虫

简单了解了BeautifulSoup并且完成了BeautifulSoup的安装后,我们就可以开始编写我们的爬虫了。

我们编写一个简单的爬虫一般需要完成以下几个步骤:
  • 确定目标
    • 确定要爬取的网页,例如本实例要爬取的是百度百科与Python相关的词条网页以及标题和简介
  • 分析目标
    • 分析目标网页的URL格式,避免抓取不相干的URL
    • 分析要抓取的数据格式,例如本实例中要抓取的是标题和简介等数据
    • 分析目标网页的编码,不然有可能在使用解析器解析网页内容时会出现乱码的情况
  • 编写代码
    • 分析完目标页面后就是编写代码去进行数据的爬取
  • 执行爬虫
    • 代码编写完成之后,自然是执行这个爬虫,测试能否正常爬取数据

开始分析本实例需要爬取的目标网页:
  • 目标:百度百科Python词条相关词条网页-标题和简介
  • 入口页:https://baike.baidu.com/item/Python/407313
  • URL格式:
    • 词条页面URL:/item/name/id 或者 /item/name/,例:/item/C/7252092 或者 /item/Guido%20van%20Rossum
  • 数据格式:
    • 标题格式:
      • &lt;dd class="lemmaWgt-lemmaTitle-title"&gt;&lt;h1&gt;***&lt;/h1&gt;***&lt;/dd&gt;
    • 简介格式:
      • &lt;div class="lemma-summary" label-module="lemmaSummary"&gt;***&lt;/div&gt;
  • 页面编码:UTF-8
分析完成之后开始编写实例代码
  • 该爬虫需要完成的目标:爬取百度百科Python词条相关1000个页面数据

首先创建一个工程目录,并在目录下创建一个python包,在该包下创建相应的模块文件,如下图:

  • spider_main:爬虫调度器程序,也是主入口文件
  • url_manager:url管理器,管理并存储待爬取的url
  • html_downloader:下载器,用于下载目标网页的内容
  • html_parser:解析器,解析下载好的网页内容
  • html_outputer:输出器,将解析后的数据输出到网页上或控制台中

爬虫调度器程序代码:

代码语言:javascript
复制
'''
    爬虫调度器程序,也是主入口文件
'''

import url_manager, html_downloader, html_parser, html_outputer

class SpiderMain(object):
    # 初始化各个对象
    def __init__(self):
        self.urls = url_manager.UrlManager()  # url管理器
        self.downloader = html_downloader.HtmlDownloader()  # 下载器
        self.parser = html_parser.HtmlParser()  # 解析器
        self.outputer = html_outputer.HtmlOutputer()  # 输出器

    # 爬虫调度方法
    def craw(self, root_url):
        # 记录当前爬取的是第几个URL
        count = 1
        # 将入口页面的url添加到url管理器里
        self.urls.add_new_url(root_url)

        # 启动爬虫的循环
        while self.urls.has_new_url():
            try:
                # 获取待爬取的url
                new_url = self.urls.get_new_url()

                # 每爬取一个页面就在控制台打印一下
                print("craw", count, new_url)

                # 启动下载器来下载该url的页面内容
                html_cont = self.downloader.download(new_url)

                # 调用解析器解析下载下来的页面内容,会得到新的url列表及新的数据
                new_urls, new_data = self.parser.parse(new_url, html_cont)

                # 将新的url列表添加到url管理器里
                self.urls.add_new_urls(new_urls)

                # 收集解析出来的数据
                self.outputer.collect_data(new_data)

                # 当爬取到1000个页面时则停止爬取
                if count == 1000:
                    break

                count += 1

            except:
                # 爬取时出现异常则在控制台中输出一段文字
                print("craw failed")

        # 输出处理好的数据
        self.outputer.output_html()

# 判断本模块是否作为入口文件被执行
if __name__ == "main":
    # 目标入口页面的URL
    root_url = "https://baike.baidu.com/item/Python/407313"
    obj_spider = SpiderMain()
    # 启动爬虫
    obj_spider.craw(root_url)

url管理器代码:

代码语言:javascript
复制
'''
    url管理器,管理并存储待爬取的url。

    url管理器需要维护两个列表,一个是
    待爬取的url列表,另一个是已爬取的
    url列表。
'''

class UrlManager(object):
    def __init__(self):
        self.new_urls = set()  # 待爬取的url列表
        self.old_urls = set()  # 已爬取的url列表

    def add_new_url(self, url):
        '''
        向管理器中添加新的url,也就是待爬取的url
        :param url: 新的url
        :return:
        '''
        # url为空则结束
        if url is None:
            return

        # 该url不在两个列表中才是新的url
        if url not in self.new_urls and url not in self.old_urls:
            self.new_urls.add(url)

    def add_new_urls(self, urls):
        '''
        向管理器中批量添加新的url
        :param urls: 新的url列表
        :return:
        '''
        if urls is None or len(urls) == 0:
            return

        for url in urls:
            self.add_new_url(url)

    def has_new_url(self):
        '''
        判断管理器中是否有待爬取的url
        :return: True 或 False
        '''
        return len(self.new_urls) != 0

    def get_new_url(self):
        '''
        从url管理器中获取一个待爬取的url
        :return: 返回一个待爬取的url
        '''
        # 出栈一个url,并将该url添加在已爬取的列表中
        new_url = self.new_urls.pop()
        self.old_urls.add(new_url)

        return new_url

下载器代码:

代码语言:javascript
复制
'''
    下载器,用于下载目标网页的内容
'''

from urllib import request

class HtmlDownloader(object):
    def download(self, url):
        '''
        下载url地址的页面内容
        :param url: 需要下载的url
        :return: 返回None或者页面内容
        '''
        if url is None:
            return None

        response = request.urlopen(url)
        if response.getcode() != 200:
            return None

        return response.read()

解析器代码:

代码语言:javascript
复制
'''
    解析器,解析下载好的网页内容
'''
import re
import urllib.parse

from bs4 import BeautifulSoup

class HtmlParser(object):
    def parse(self, page_url, html_cont):
        '''
        解析下载好的网页内容
        :param page_url: 页面url
        :param html_cont: 网页内容
        :return: 返回新的url列表及解析后的数据
        '''
        if page_url is None or html_cont is None:
            return

        soup = BeautifulSoup(html_cont, 'html.parser')
        new_urls = self._get_new_urls(page_url, soup)
        new_data = self._get_new_data(page_url, soup)

        return new_urls, new_data

    def _get_new_urls(self, page_url, soup):
        '''
        得到新的url列表
        :param page_url:
        :param soup:
        :return:
        '''
        new_urls = set()

        # 词条页面URL:/item/name/id 或者 /item/name/,例:/item/C/7252092 或者 /item/Guido%20van%20Rossum
        links = soup.find_all('a', href=re.compile(r"/item/(.*)"))
        for link in links:
            new_url = link['href']
            # 拼接成完整的url
            new_full_url = urllib.parse.urljoin(page_url, new_url)
            new_urls.add(new_full_url)

        return new_urls

    def _get_new_data(self, page_url, soup):
        '''
        解析数据,并返回解析后的数据
        :param page_url:
        :param soup:
        :return:
        '''
        # 使用字典来存放解析后的数据
        res_data = {}

        # url
        res_data['url'] = page_url

        # 标题标签格式:<dd class="lemmaWgt-lemmaTitle-title"><h1>***</h1>***</dd>
        title_node = soup.find('dd', class_='lemmaWgt-lemmaTitle-title').find('h1')
        res_data['title'] = title_node.get_text()

        # 简介标签格式:<div class="lemma-summary" label-module="lemmaSummary">***</div>
        summary_node = soup.find('div', class_='lemma-summary')
        res_data['summary'] = summary_node.get_text()

        return res_data

输出器代码:

代码语言:javascript
复制
'''
    输出器,将解析后的数据输出到网页上
'''

class HtmlOutputer(object):
    def __init__(self):
        # 存储解析后的数据
        self.datas = []

    def collect_data(self, data):
        '''
        收集数据
        :param data:
        :return:
        '''
        if data is None:
            return

        self.datas.append(data)

    def output_html(self):
        '''
        将收集的数据以html的格式输出到html文件中,我这里使用了Bootstrap
        :return:
        '''
        fout = open('output.html', 'w', encoding='utf-8')

        fout.write("<!DOCTYPE html>")
        fout.write("<html>")
        fout.write('<head>')
        fout.write('<meta charset="UTF-8" />')
        fout.write(
            '<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"/>')
        fout.write('</head>')
        fout.write("<body>")
        fout.write(
            '<div style="width: 1000px;margin: auto" class="bs-example" data-example-id="bordered-table" ><table class="table table-bordered table-striped" >')
        fout.write(
            '<thead><tr style="height: 70px;font-size: 20px"><th style="text-align: center;vertical-align: middle;width: 60px">#</th><th style="text-align: center;vertical-align: middle;width: 150px">URL & 标题</th><th style="text-align: center;vertical-align: middle;">简介</th></tr></thead><tbody>')

        num = 0
        for data in self.datas:
            fout.write("<tr>")
            fout.write("<th style='text-align: center;vertical-align: middle;' scope='row'>%d</th>" % num)
            fout.write("<td style='text-align: center;vertical-align: middle;'><a href=%s>%s</a></td>" % (
                data['url'], data['title']))
            fout.write("<td>%s</td>" % data['summary'])
            fout.write("</tr>")
            num += 1

        fout.write("</tbody></table></div>")
        fout.write("</body>")
        fout.write("</html>")

        fout.close()

运行效果:

控制台输出:

生成的html文件:

至此,我们一个简单的爬虫就完成了。

源码GitHub地址:

https://github.com/Binary-ZeroOne/easy-spider

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/02/23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • BeautifulSoup模块介绍和安装
  • 实例爬虫
    • 我们编写一个简单的爬虫一般需要完成以下几个步骤:
      • 开始分析本实例需要爬取的目标网页:
        • 分析完成之后开始编写实例代码
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档