第六天:

使用Beautiful Soup解析网页

 

通过requests库已经可以抓到网页源码,接下来要从源码中找到并提取数据。Beautiful Soup是python的一个库,其最主要的功能是从网页中抓取数据。Beautiful Soup目前已经被移植到bs4库中,也就是说在导入Beautiful Soup时需要先安装bs4库。

 

安装好bs4库以后,还需要安装lxml库。如果我们不安装lxml库,就会使用python默认的解析器。尽管Beautiful Soup支持python标准库中的HTML解析器也支持一些第三方解析器,但是lxml库具有功能更强大的,速度更快的特点,因此,环康老师推荐安装lxml库。安装好第三方库后,开启我们的Beautiful Soup之旅吧。

 

Beautiful Soup库能够轻松解析网页信息,它被集成在bs4库中,需要时可以从bs4库中调用:

      from bs4 import BeautifulSoup

 

我们通过抓取中国旅游网:http://www.cntour.cn/
来学习Beautiful Soup库
 
首先我们导入requests库和 BeautifulSoup库
import requests
from bs4 import BeautifulSoup
 
定义中国旅游网的url
url = 'http://www.cntour.cn/'
 
 
发送请求获取响应,HTML文档将被转换成Unicode编码格式,然后BeautifulSoup定义lxml解析器进行解析,解析后便将复杂的HTML文档转换成树形结构,并且每个节点都是python对象,这里将解析后的文档存储到新建的变量soup中,代码如下:
strhtml = requests.get(url)
soup = BeautifulSoup(strhtml.text, 'lxml')
 
接下来用select(选择器)定位数据,定位数据时需要使用浏览器的开发者模式,将鼠标停留在对应的数据位置并右击,点击检查。
对应着左侧高亮的数据文本右击,在弹出的快捷菜单选择:
Copy è Copy Selector 便可自动复制路径:
 
#main > div > div.mtop.firstMod.clearfix > div.centerBox > ul.newsList > li:nth-child(1) > a
 
由于这条路径是选中的第一条路径,而我们需要获取所有头条新闻,因此将li:nth-child(1)中冒号(包含冒号)后面的部分删掉,代码如下
#main > div > div.mtop.firstMod.clearfix > div.centerBox > ul.newsList > li > a
使用soup.select引用这个路径:

data = soup.select('#main > div > div.mtop.firstMod.clearfix > div.centerBox > ul.newsList > li > a')

 
尝试print一下data。
 
 
至此,获得了一段目标的HTML代码,但是没有把数据提取出来,接下来在pycharm中输入一下代码:
(因为data是一个列表)

for item in data:
    result= {
        'title':item.get_text(),  # 提取标签的正文用get_text()方法
       
'link':item.get('href'# 提取标签中的属性用get()方法,在括号中指定要提取的属性名称
   
}
    print(result)

 

输出为:


 
 
 
爬虫攻防战

    爬虫是模拟人的浏览访问行为,进行数据的批量抓取。当抓取的数量逐渐增大时,会给被访问的服务器造成很大的压力,甚至有可能崩溃,换句话说就是,服务器是不喜欢有人抓取自己的数据的,那么网站就会针对这些爬虫者,采取一些反爬策略。

    服务器第一种识别爬虫的方式就是通过检查连接的useragent来识别到底是浏览器访问,还是代码访问的。如果是代码访问的话,访问量增大时,服务器就会直接封掉来访IP。

    那么应对这种初级的反爬机制,我们应该采取何种举措?

    在进行访问时,我们开发者环境下不仅可以找到URL、

Form Data,还可以在Request header中构造浏览器的请求头,封装自己。服务器识别浏览器访问的方法就是判断keyword是否为

Request header下的User-Agent,因此我们只需要构造这个请求头的参数。创建请求头部信息即可:

 

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
strhtml = requests.get(url, headers=headers)

 

写到这里,很多人会认为修改User-Agent太简单。确实很简单,但是正常人1秒钟看一个图,而爬虫1秒钟可以抓取好多张图,比如1秒抓取上百张图,那么服务器的压力必然会增大。也就是说,如果在一个IP下访问下载图片,这个行为不符合正常人的行为,肯定要被封IP。其原理也很简单,就是统计每个IP的访问频率,该频率超过阈值,就会返回一个验证码,如果真的是用户访问的话,用户就会填写,然后继续访问,如果是代码访问的话,就会被封IP。

这个问题的解决方案有两个,第一个就是常用的增设延时,每3秒钟抓取一次:

    import time

    time.sleep(3)

但是,我们些爬虫的目的是为了高效批量的抓取数据,这里设置3秒钟抓去一次,效率未免太低。其实,还有一个更重要的解决办法,那就是从本质上解决问题。

    不管如何访问,服务器的目的就是查出那些为代码访问,然后封IP。解决办法:为避免被封IP,在数据采集时经常会使用代理,当然requests也有相应的proxies属性

    首先,构建自己的代理IP池,将其以字典的形式赋值给proxies,然后传给requests,代码如下:

 

import requests
from bs4 import BeautifulSoup

url = 'http://www.cntour.cn/'

proxies = {
    'https': 'https://116.113.27.170:47849'
}

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
strhtml = requests.get(url, headers=headers, proxies=proxies)
soup = BeautifulSoup(strhtml.text, 'lxml')
data = soup.select('#main > div > div.mtop.firstMod.clearfix > div.centerBox > ul.newsList > li > a')
print(data)

for item in data:
    result= {
        'title':item.get_text(),  # 提取标签的正文用get_text()方法
       
'link':item.get('href'# 提取标签中的属性用get()方法,在括号中指定要提取的属性名称
   
}
    print(result)