整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

技术分享|利用Python和BeautifulSoup进行网页爬取(新手教程)

据观世界

在大数据、人工智能时代,我们通常需要从网站中收集我们所需的数据,网络信息的爬取技术已经成为多个行业所需的技能之一。而Python则是目前数据科学项目中最常用的编程语言之一。使用Python与BeautifulSoup可以很容易的进行网页爬取,通过网站爬虫获取信息可以帮助企业或个人节省很多的时间和金钱。学习本文之后,我相信大部分新手都能根据自己的需求来开发出相应的网页爬虫。

基础条件

  • 了解简单的Python编程规则(Python 3.x)
  • 了解简单的网页Html标签

如果您是完全的新手也不用担心,通过本文您可以很容易地理解。

安装所需包

首先,您需要先安装好Python 3.x,Python安装包可以从python.org下载,然后我们需要安装requests和beautifulsoup4两个包,安装代码如下:

$ pip install requests

$ pip install beautifulsoup4

爬取网页数据

现在我们已经做好了一切准备工作。在本教程中,我们将演示从没被墙的维基百科英文版页面中获取历届美国总统名单。

转到此链接(https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States#Presidents)并右键单击包含有关美国总统的所有信息的表格,然后单击"检查"选项(我用的是Chrome浏览器,其他浏览器右键单击页面后也会具有类似或相同的选项)。

由下图可知,表格的内容位于class属性为wikitable的table标签下,我们需要了解这些标签信息来获取我们所需内容。

了解网页信息之后,我们就可以编写代码了。首先,我们要导入我们安装的包:

import requests

from bs4 import BeautifulSoup

为了获取网页数据我们要使用requests的get()方法:

url = "https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States"

page = requests.get(url)

检查http响应状态,来确保我们能正常获取网页,如果输出状态代码为200则为正常:

print(page.status_code)

现在我们已经获取了网页数据,让我们看看我们得到了什么:

print(page.content)

上面的代码会显示http相应的全部内容,包括html代码和我们需要的文本数据信息。通过使用beautifulsoup的prettify()方法可以将其更美观的展示出来:

soup = BeautifulSoup(page.content, 'html.parser')

print(soup.prettify())

这会将数据按照我们上面"检查"中看到的代码形式展示出来:

目前获得的是包含我们所需数据的网页源码,我们接下来要将有用的信息提取出来。上面已经知道我们所需的内容在class属性为wikitable的table标签下,因此,接下来我们将使用bs4对象的find方法提取table标签中的数据,此方法返回bs4对象:

tb = soup.find('table', class_='wikitable')

table标签下有很多嵌套标签,通过网页检查中的代码可以发现,我们最终是需要获得title元素中的文本数据,而title元素位于a标签下,a标签位于b标签下,b标签位于table标签下。为了获取所有我们所需的数据,我们需要提取table标签下的所有b标签,然后找到b标签下的所有a标签,为此,我们使用find_all方法来迭代获取所有b标签下的a标签:

for link in tb.find_all('b'):

name = link.find('a')

print(name)

这将获取所有a标签下的数据:

可以看出,这并不是我们所要的最终结果,其中掺杂着html代码,不用担心,我们只需为上面的代码添加get_text()方法,即可提取出所有a标签下title元素的文本信息,代码改动如下:

for link in tb.find_all('b'):

name = link.find('a')

print(name.get_text('title'))

最终获得所有总统的名单如下:

George Washington

John Adams

Thomas Jefferson

James Monroe

...

...

Barack Obama

Donald Trump

将Python代码合并在一起:

import requests

from bs4 import BeautifulSoup

url = "https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States"

page = requests.get(url)

soup = BeautifulSoup(page.content, 'html.parser')

tb = soup.find('table', class_='wikitable')

for link in tb.find_all('b'):

name = link.find('a')

print(name.get_text('title'))

仅仅9行代码,我们就实现了网页信息的爬取,我相信您从中已经学到了Python、html的基础知识,并且已经懂得了网页爬虫的基本原理,如果想更深入的学习网页爬虫,你可以尝试对以上代码进行改进,尝试爬取更多的网站,欢迎与我留言交流。

crapy无疑是优秀的爬虫框架,它使用的是类似Requests的方式来爬取网页,也就是说它爬取的静态页面,但是现实却是大部分的网站都是动态的,我们看到的正常页面都是浏览器渲染后的结果,如果直接使用scrapy爬取网页很可能得不到期望的数据。一种想当然的方式,就是通过自定义Middleware使用类似selenium这种模拟浏览器行为的方式来直接拿到渲染好的html内容,这样当然是可行的,scrapy也可以轻松和selenium对接,不足之处是增加了资源的消耗。

还有一种直达本质的方式,就是找到数据的源头,有的通过ajax请求获取数据源,有的将数据源放到js代码中,通过浏览器开发工具分析交互过程找到数据源头后,如果是ajax请求就直接请求对应的接口就可以拿到数据,本文将介绍数据源在js文件中的提取方法

举个例子

这里以某文库搜索结果为例,如果直接scrapy爬取页面是看不到下图中的搜索结果的,分析页面结构发现源头数据在script脚本中,脚本中基本上就是类似json的数据对象,只要拿到js脚本的对象数据就等于得到了搜索结果。

安装chompjs

python是可以解析js脚本并获取其中的数据对象的,这里推荐chompjs这个库,chompjs可以直接将js中的数据对象返回给python,非常的方便,使用前需要使用pip install chompjs安装此库。需要注意的是,chompjs的核心代码使用c来编写,因此安装chompjs需要事先安装c++构建工具。

1. 如果事先没有安装c++构建工具会报错

2. 访问上面错误提示中的链接下载c++构建工具并安装

安装成功之后再次执行pip install chompjs安装成功

scrapy shell

scrapy shell可以使用交互方式快速验证提取数据的代码,不需要一次次地运行爬虫验证,非常的高效,接下来就演示如何使用scrapy shell把js中的数据提取出来。
启动scrapy shell

scrapy shell "https://wenku.baidu.com/search?word=python&lm=0&od=0&fr=top_home&ie=utf-8&_wkts_=1711155481643&wkQuery=python"

从返回的结果来看,response已经获取到了爬取的页面内容,接下来主要使用response进行数据提取

我们已经知道数据源在script中,直接使用response.css('script')可以获取所有的script,我们需要的是第二个script中的脚本

直接取出script中的js脚本,response.css("script::text")[1].get()

最后就是导入chompjs库,执行js代码并获取返回的数据对象

import chompjs
data = chompjs.parse_js_object(js_code)

data是dict字典对象,需要的数据在'sulaData'这个key中,至此,大功告成啦。

简单做个总结,我认为使用scrapy写爬虫根本不需要用到像selenium这样的模拟浏览器下载器,解决的办法就是找到数据的源头,没有数据源动态网站也无从渲染。不过,这里忽略了一个重要的环节,就是登录,不同平台登录机制也不同,各种各样的验证码让人头疼,正面硬刚很可能碰的头破血流,因此我不建议把登录部分写在爬虫里,一种更好的方案是单独开发一个cookie池服务,cookie池服务负责管理所有平台的用户登录,不能自动登录的就提供手工登录,并通过web api提供随机获取cookie,scrapy通过调用cookie池的api来获取cookie实现模拟已登录用户状态。

eautifulsoup介绍:

  1. 是一个高效的网页解析库,可以从HTML或XML文件中提取数据
  2. 支持不同的解析器,比如,对HTML解析,对XML解析,对HTML5解析
  3. 就是一个非常强大的工具,爬虫利器
  4. 一个灵活又方便的网页解析库,处理高效,支持多种解析器
  5. 利用它就不用编写正则表达式也能方便的实现网页信息的抓取


第一步:安装BeautifulSoup4,lxml

 pip install BeautifulSoup4
    BeautifulSoup 是一个可以从HTML或XML文件中提取数据的Python库
pip install lxml
    lxml 是一种使用 Python 编写的解析库,可以迅速、灵活地处理 XML 和 HTML


第二步:导包,from bs4 import BeautifulSoup


第三步:实例化对象

html = """
<html>
    <head>
        <title>The Dormouse's story</title>
    </head>
    <body>
        <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">
                <span>Elsie</span>
            </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>
"""
soup = BeautifulSoup(html, 'lxml')  # h:要解析的内容  lxml:解析器
知识补充:print(soup.prettify())  # 代码补全


第四步:打印

一、通过标签选取,会返回包含标签本身及其里面的所有内容

# print(soup.head) # 包含head标签在内的所有内容
# print(soup.p) # 返回匹配的第一个结果

1.print(soup.head)打印结果:

<head>

<title>The Dormouse's story</title>

</head>

2.print(soup.p)打印结果:

<p class="title" name="dromouse"><b><span>The Dormouse's story</span></b></p>


二、打印标签中间的文本内容,不包含<>

# .string是属性,作用是获取字符串文本
print(soup.html.head.title.string)
print(soup.title.string)

打印结果都为:The Dormouse's story


三、打印标签名

 .name --获取标签本身名称  
 print(soup.title.name)

打印结果为:title


四、打印属性的值

.attrs[]  --通过属性拿属性的值 
print(soup.p.attrs['name'])# 获取p标签name属性的属性值
print(soup.a.attrs['id']) # 获取p标签id属性的属性值

print(soup.a['id']) #第二种写法
print(soup.p['class']) # 以列表得形式保存
print(soup.a['href'])  # 也是只返回第一个值

1.print(soup.p.attrs['name'])打印结果:

dromouse

2.print(soup.p.attrs['id'])和print(soup.a['id'])打印结果:

link1

3.print(soup.p['class'])打印结果:

['title', 'asdas']

4.print(soup.a['href'])打印结果:

http://example.com/elsie


五、打印父标签下的所有子标签

.contents 获取标签子节点,以列表形式返回
.children 获取子节点,返回的是一个list类型的迭代器

print(soup.body.contents)  # a是p的子节点,获取P标签所有子节点内容 返回一个list
print(soup.body.children) #返回的是一个list类型的迭代器

1.print(soup.body.contents)的打印结果:

['\n', <p class="title asdas" name="dromouse"><b><span>The Dormouse's story</span></b></p>, '\n', <p class="story">Once upon a time there were three little sisters; and their names were

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,

<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and

<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;

and they lived at the bottom of a well.</p>, '\n', <p class="story">...</p>, '\n']

2.print(soup.body.children)的打印结果:

<list_iterator object at 0x000002035ECC7088>


.children 获取子节点讲解

1.和for循环一起使用

for i in soup.p.children:
print(i)

打印结果: