爬虫入门(五)抓取网页数据

前面已经实现了获取整个网页HTML的功能,并且在获取HTML的基础上,加上一些诸如:下载出错自动重试、用户代理、服务器代理、爬取深度、避免重爬、id遍历、链接遍历等进阶功能。而且在处理过程中也初步使用了正则表达式。

但是前面我们获取的HTML中的数据很多,其中大部分是我们不需要的。因此在本节中,我们要介绍对比三种抓取数据的方式,也可以叫选择器,并给出他们的性能对比,以供选择。

1.分析网页

在抓取一个网页之前的具体内容之前,我们首先应该分析一下网页的结构。网页的构成是一种标签化的结构语言编写的。这些标签也可以看成一个个元素,比如标题元素、表单元素、表格元素、行元素、列元素。通常我们要抓取的数据就在这些元素内。

通过F12或者右键查看页面源码,通过审查元素找到我们感兴趣的内容

以示例网站为例,打开日本的网页地址:

http://example.python-scraping.com/places/default/view/Japan-108

按F12打开开发者工具,元素审查,找到自己感兴趣的内容,比如日本的面积、简称、语言等,

发现这些元素位于一个表单form内,行标签内的列元素,进一步发现有ID属性,有class属性 通过这两个字段就可以精确定位到某行某列。

爬虫入门(五)抓取网页数据_第1张图片

 2.抓取方式一正则表达式

正如前面编写链接爬虫时那样,使用正则表达式匹配页面内容不失为一个好的方式,阅读编写正则表达式是一个爬虫程序员必备的技能

2.1本例中用到的匹配规则如下:

\b 匹配一个单词的边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。

例如:

“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;

“\b1_”可以匹配“1_23”中的“1_”,但不能匹配“21_3”中的“1_”。

r'':Python中字符串前面加上 r 表示原生字符串(rawstring)。不使用r,那么匹配时候需要4个反斜杠,正则需要转化一次,python解释器需要转化一次,使用r就不用使用转义字符

        如: >>> re.findall("abc\b","adsssa abc ")                 输出:[]

                 >>> re.findall(r"abc\b","adsssa abc ")               输出:['abc']

在页面截图中,可以看到数据列

爬虫入门(五)抓取网页数据_第2张图片

查看元素发现他们储存在class="w2p_fw的中的 377,835 square kilometres

因此构造一个正则表达式r'(.*?)'可以完整匹配出所要的内容

假如想精准定位日本的人口,定位id为places_population__row的标签元素的html格式如下

127,288,000

因为网站有可能单引号双引号混用,或者多按了两个空格,因此引号用['"]匹配,空格用/s*匹配,最终构造的正则式为: r'.*?(.*?)'

 

import re
from scrap_01 import GetData            #该文件为在前两章中编写的获取网页HTML的方法
url = 'http://example.python-scraping.com/places/default/view/Japan-108'
html = GetData(url)
data = re.findall(r'(.*?)',html)
print(data)
data2 = re.findall('''.*?(.*?)''',html)
print(data2)

 3.抓取方式一Beautiful Soup

从上个例子可以得出正则表达式存在这一定的缺点,如正则式难以构造、可读性差等 不过我们还有其他的库可以用,比如流行的BeautifulSoup和强大的lxml模块。

3.1安装:pip install beautifulsoup4

3.2使用步骤:

        1.将获得的HTML文档解析为soup文档

         Beautiful Soup能对一些不规范编写HTMl格式的文档进行修正,如标签闭合,属性值引号缺失

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

         #'html.parser'是BeautifulSoup默认的解析器,有时并不怎么好用,修正Html的能力有限。

        这里推荐使用html5lib或者lxml(下文中介绍)

         soup = BeautifulSoup(html, 'html5lib')

         2.使用find或者findall方法来定位元素

         find('tag','regex'),tag指标签类型,regex可以是attrs属性字典或者正则表达式,返回的是html形式的元素标签

         find_all('tag','regex'),返回的是元素标签集合

         td = soup.find('tr', attrs={'ID':'places_population__row'}) print(td.find('td'))                           

         print(td.find('td').text)

        3.获取标签内的文本

         对于就是标签内只有不含有其他标签子节点,那么这个 tag 可以使用 result.string 得到文本,也可以用 result.text 获得文本 如果 tag 包含了多个子节点,tag 就无法确定 result.string 方法应该调用哪个子节点的内容, result.string 的输出结果是 None

from bs4 import BeautifulSoup
import html5lib

url = 'http://example.python-scraping.com/places/default/view/Japan-108'
html = GetData(url)
soup = BeautifulSoup(html, features="html5lib")
tr = soup.find(attrs={'id':'places_population__row'})       #find()返回的是html形式的元素标签
print(tr)
td = tr.find_all(attrs={'class':'w2p_fw'})                  #find_all返回的是元素标签集合
print(td)
print(td[0].text)

4.抓取方式一Lxml

        xml是XML和HTML的解析器,其主要功能是解析和提取XML和HTML中的数据; lxml和正则一样,也是用C语言实现的,是一款高性能的python HTML、XML解析器,也可以利用XPath语法,来定位特定的元素及节点信息 在解析HTML代码的时候,如果HTML代码不规范或者不完整,lxml解析器会自动修复或补全代码,从而提高效率

        4.1解析器解析html

         lxml通常都是用etree解析,这里用的是是lxml.html,其实etree功能更多,lxml.html专解析html,二者解析html的结果相差不大

         lxml.html:

                  import lxml.html html = lxml.html.fromstring(sample)

                  result2 = lxml.html.tostring(html,pretty_print =True)

                  print(result2)

         etree:

                 from lxml import etree html = etree.HTML(sample)

                 result1 = etree.tostring(html,pretty_print =True)

                 print(result1)

附结构化输出概念: python中的pprint模块负责以合适的格式打印便于阅读的行块。它使用换行和缩进以明确的方式打印数据。 “pprint”模块它将对象格式化为可读的格式,每行都有适当的宽度。它带有可调节的宽度限制,以使它更容易为用户。 它将所有元素转换为可以用Python常量表示的字符串,并以美观的格式打印它们。pprint函数返回可以在解释器中作为输入运行的输出。 而且,通过解析字符串更容易将其转换回该类型。

pprint的使用方法:

         import pprint                 

         #使用.pprint()对象或实例化我们自己的pprint对象PrettyPrinter()。

         pprint.pprint(['Radha', 1, 'Hari', 'Simesh', 25, 847])

         my_pprint = pprint.PrettyPrinter()

         my_pprint.pprint(['Radha', 1, 'Hari', 'Simesh', 25, 847])

而在解析器lxml.html.tostring(html,pretty_print =True)中的pretty_print =True表达的即是要格式化输出 的意思

4.2.选择(定位)元素

选择的方法包括有:

        XPath选择器、find()方法或者CSS选择器。

这里使用的是CSS选择器,因为它更加简介,并且能解析动态内容。

安装:pip install cssselect

使用:

        几个常用的选择器:

         *:通用选择器

         X:元素选择器

         #X:ID选择器

         .X:类选择器

         X Y:后代选择器

         X > Y:子元素选择器

         X[title]:简单属性选择器

         X[href="foo"]:精准属性值选择器

         X[href*="xxx"]:匹配部分属性值选择器

         X[href^="http"]:匹配属性值开头的选择器

         X[href$=".jpg"]:匹配属性值结尾的选择器

         X[data-*="foo"]:自定义属性选择器

         参考链接:https://blog.csdn.net/Ed7zgeE9X/article/details/102812665

故我们所要构造的选择器:

tr#places_population__row > td.w2p_fw

意思为选择id为places_population__row的tr元素的子元素中类Class=w2p_fw的元素

from lxml.html import fromstring,tostring
import cssselect
url = 'http://example.python-scraping.com/places/default/view/Japan-108'
html = GetData(url)
tree = fromstring(html)
td = tree.cssselect('tr#places_population__row > td.w2p_fw')[0]
print(type(td))
population = td.text_content()
print(population)

4.3使用控制台+jQuery测试CSS选择器

jquery是一个优秀的javascript的轻量级框架,兼容css3和各大浏览器。 Jquery就是1个js文件,只不过它对JS进行了简化。

        jQuery 库包含以下特性:

         HTML 元素选取

         HTML 元素操作

         CSS 操作

         HTML 事件函数

         JavaScript 特效和动画

         HTML DOM 遍历和修改

         AJAX Utilities

在浏览器的开发者工具中的控制台上可以测试自己编写的CSS选择器

$()或jQuery() 称之为jq选择器环境,在里面加上引号填写相关选择器即可,就可以获取匹配的元素。

4.4XPath选择器

        CSS无法应对HTML非常不完整或者存在格式不当的问题时,这时CSS选择器可能会无法工作,尽管在解析时已经尽最大的努力修复html了, 但还是有很大的可能无法工作。这种情况下XPath可以帮助你基于页面的层次结构关系构建非常明确的选择器。

        XPath选择器:

选取节点:
表达式       描述                        示例              结果
nodename   选取此节点的所有子节点            body           选取 body 元素的所有子节点
/          从根节点选取                 /html          选取根元素 html
//         匹配选择的当前节点,不考虑位置    //img          选取所有 img 元素,而不管它们在文档的位置
.          选取当前节点                 ./img          选取当前节点下的 img 节点
..         选取当前节点的父节点         ../img         选取当前节点的父节点下的 title
@          选取属性                       //a[@href=”image1.html”]   选取所有 href 属性为 “image1.html” 的 a 节点
                                        //a/@href       获取所有 a 节点的 href 属性的值
使用谓词:
谓语用来查找某个特定的节点或者包含某个指定的值的节点,谓语嵌在方括号中。
路径表达式                  结果
//body//a[1]               选取属于 body 子元素的第一个 a 元素
//body//a[last()]          选取属于 body 子元素的最后一个 a 元素
//a[@href]                 选取所有拥有名为 href 的属性的 a 元素
//a[@href=’image2.html’]   选取所有 href 属性等于 “image2.html” 的 a 元素

常用函数:
contains(s1,s2)             如果s1中包含s2,返回True,否则返回False
text()                      获取文本内容
starts-with()               从起始位置匹配字符串  

 参考链接:https://blog.csdn.net/qq_32942549/article/details/78400675

url = 'http://example.python-scraping.com/places/default/view/Japan-108'
html = GetData(url)
tree = fromstring(html)
population = tree.xpath('//tr[@id="places_population__row"]/td[@class="w2p_fw"]/text()')[0]
print(population)

4.5lxml和家族树
家族树就是html文档的dom树,lxml提供了访问网页中元素父亲、兄弟、孩子元素的方法
方式:
获取元素对象
table = tree.xpath('//table')[0]
1.得到孩子元素
    table.getchildren()
2.得到前一个兄弟元素
    table.getprevious()
3.得到后一个兄弟元素
    table.getnext()
4.得到父元素
    table.getparent()

5.性能对比

由于lxml和re正则是用C语言写的,而BeautifulSoup是用python写的
所以理论上来说BeautifulSoup的速度是相对要慢的多的
抓取方式          性能          使用难度            安装难度
正则表达式         快           困难                简单(内置模块)
BeautifulSoup    慢            简单                简单(纯python)
Lxml             快            简单                相对困难(3.9以后版本也简单)
因此通常情况下lxml是最佳选择,因为该方法即快速又健壮

你可能感兴趣的:(爬虫学习之旅,python,爬虫)