BeautifulSoup模块介绍和安装
- BeautifulSoup
- BeautifulSoup是Python的第三方库,用于从HTML或XML中提取数据,通常用作于网页的解析器
- BeautifulSoup官网: https://www.crummy.com/software/BeautifulSoup/
- 官网文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/
- 中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
BeautifulSoup安装很简单,我们可以直接使用pip来安装BeautifulSoup,安装命令如下:
pip install beautifulsoup4
如果使用的IDE是Pycharm的话,安装更简单,直接编写导入模块的语句:import bs4
,然后会报错,提示模块不存在,接着按 alt + 回车,会出现错误修正提示,最后选择安装模块即可自动安装。
安装完成之后编写一段测试代码:
import bs4
print(bs4)
如果执行这段代码,并且正常输出没有报错则代表已经安装成功。
BeautifulSoup的语法:
访问节点信息:
语法格式:
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()
实际的测试代码:
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
- 数据格式:
- 标题格式:
<dd class="lemmaWgt-lemmaTitle-title"><h1>***</h1>***</dd>
- 简介格式:
<div class="lemma-summary" label-module="lemmaSummary">***</div>
- 页面编码:UTF-8
分析完成之后开始编写实例代码
- 该爬虫需要完成的目标:爬取百度百科Python词条相关1000个页面数据
首先创建一个工程目录,并在目录下创建一个python包,在该包下创建相应的模块文件,如下图:
- spider_main:爬虫调度器程序,也是主入口文件
- url_manager:url管理器,管理并存储待爬取的url
- html_downloader:下载器,用于下载目标网页的内容
- html_parser:解析器,解析下载好的网页内容
- html_outputer:输出器,将解析后的数据输出到网页上或控制台中
爬虫调度器程序代码:
'''
爬虫调度器程序,也是主入口文件
'''
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管理器代码:
'''
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
下载器代码:
'''
下载器,用于下载目标网页的内容
'''
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()
解析器代码:
'''
解析器,解析下载好的网页内容
'''
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
输出器代码:
'''
输出器,将解析后的数据输出到网页上
'''
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://blog.51cto.com/zero01/2072442