第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Scrapy 入門教程 Scrapy 入門教程 Scrapy 默認(rèn)的網(wǎng)頁解析器 Xpath

Scrapy 默認(rèn)的網(wǎng)頁解析器 Xpath

Xpath 是 Scrapy 框架中默認(rèn)的網(wǎng)頁解析器,只有掌握了 Xpath 選擇器,我們才能快速從網(wǎng)頁元素中提取我們想要的數(shù)據(jù)。

1. xpath 選擇器介紹

首先來看看 Xpath 的字面介紹:

XPath 即為 XML 路徑語言(XML Path Language),它是一種用來確定XML文檔中某部分位置的語言。 XPath 基于 XML 的樹狀結(jié)構(gòu),提供在數(shù)據(jù)結(jié)構(gòu)樹中找尋節(jié)點(diǎn)的能力。XQuery 和 XPointer 均構(gòu)建于 XPath 表達(dá)式之上。

來看看 xpath 最常用的路徑表達(dá)式規(guī)則:

表達(dá)式 描述
nodename 選擇此元素的所有子節(jié)點(diǎn)
/ 從根節(jié)點(diǎn)開始選擇
// 從匹配選擇的當(dāng)前節(jié)點(diǎn)選擇文檔中的節(jié)點(diǎn)
. 當(dāng)前節(jié)點(diǎn)
當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)
@ 選取屬性

來看下面幾個(gè)例子:

路徑表達(dá)式 含義
p 選擇所有 p 節(jié)點(diǎn)
//body 選擇所有的body元素節(jié)點(diǎn)/
//*[@class=“red-color”]/… 選擇所有class屬性值為 “red-color” 節(jié)點(diǎn)的父節(jié)點(diǎn)

在 xpath 中可以使用通配符來提取相關(guān)節(jié)點(diǎn)元素:

路徑表達(dá)式 含義
//* 找出所有節(jié)點(diǎn)
//*[@*] 匹配任何有屬性的節(jié)點(diǎn)
//*[@class=“red-color”] 提取所有class屬性值為 “red-color” 的節(jié)點(diǎn)

另外,在 xpath 中我們還可以使用運(yùn)算符,來輔助選取節(jié)點(diǎn):

路徑表達(dá)式 含義
//div | //p 選取div或者p元素的節(jié)點(diǎn)
//p[1 + 1]/text() 獲取第二個(gè)p元素節(jié)點(diǎn)的文本值
//*[@value > 10] 找出所有 value 值大于10的節(jié)點(diǎn)

其中 xpath 支持的表達(dá)式除了 +- *、divmod 等基本運(yùn)算符外,還有比較運(yùn)算符,如 =、!=、>=、<=、>> 、andor等。

在 xpath 中有一個(gè)叫做軸的概念,表示相對(duì)于當(dāng)前節(jié)點(diǎn)的節(jié)點(diǎn)集。下面是一些基本軸的定義:

軸名稱 含義
ancestor 選取當(dāng)前節(jié)點(diǎn)的所有先輩(父、祖父等)
ancestor-or-self 選取當(dāng)前節(jié)點(diǎn)的所有先輩(父、祖父等)以及當(dāng)前節(jié)點(diǎn)本身
attribute 選取當(dāng)前節(jié)點(diǎn)的所有屬性
child 選取當(dāng)前節(jié)點(diǎn)的所有子元素
descendant 選取當(dāng)前節(jié)點(diǎn)的所有后代元素(子、孫等)
descendant-or-self 選取當(dāng)前節(jié)點(diǎn)的所有后代元素(子、孫等)以及當(dāng)前節(jié)點(diǎn)本身
following 選取文檔中當(dāng)前節(jié)點(diǎn)的結(jié)束標(biāo)簽之后的所有節(jié)點(diǎn)
following-sibling 選取文檔中當(dāng)前節(jié)點(diǎn)的結(jié)束標(biāo)簽之后的所有同級(jí)節(jié)點(diǎn)
parent 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)
preceding 選取文檔中當(dāng)前節(jié)點(diǎn)的開始標(biāo)簽之前的所有節(jié)點(diǎn)
preceding-sibling 選取當(dāng)前節(jié)點(diǎn)之前的所有同級(jí)節(jié)點(diǎn)
self 選取當(dāng)前節(jié)點(diǎn)

軸的用法是:軸名稱::節(jié)點(diǎn)測試。來看下面幾個(gè)例子:

路徑表達(dá)式 含義
//body/div[2]/following-sibling::* body節(jié)點(diǎn)下第二個(gè)div節(jié)點(diǎn)之后的所有同級(jí)節(jié)點(diǎn)
//body/p[1]/child::span[last()]/text() body節(jié)點(diǎn)下的第一個(gè)p節(jié)點(diǎn)下的最后一個(gè)span子節(jié)點(diǎn)的文本值
//body/p[1]/span/child::text() body節(jié)點(diǎn)下的第一個(gè)p節(jié)點(diǎn)下的所有span子節(jié)點(diǎn)的文本值
//body/p/attribute::* body節(jié)點(diǎn)下所有p節(jié)點(diǎn)的屬性值

最后,在 xpath 中還有一些輔助我們更好搜索節(jié)點(diǎn)的函數(shù):

函數(shù) 含義
starts-with() 獲取某個(gè)字符串開頭的節(jié)點(diǎn)
contains() 包含某個(gè)字符串的節(jié)點(diǎn),可以是屬性包含、文本包含等等
text() 獲取節(jié)點(diǎn)的文本值

上述輔助函數(shù)的實(shí)例如下:

路徑表達(dá)式 含義
//p[contains(@class, “red”)] 獲取class屬性值包含"red"的所有p節(jié)點(diǎn)值
‘//span[contains(text(), “藍(lán)色”)]/text()’ 獲取文本值包含"藍(lán)色"的所有span節(jié)點(diǎn)的文本
‘//span[starts-with(text(), “藍(lán)”)]/text()’ 獲取文本值以"藍(lán)"開頭的所有span節(jié)點(diǎn)的文本

此外,當(dāng)然還有許多方面沒有講到,后續(xù)會(huì)在實(shí)戰(zhàn)中進(jìn)行說明。上面的基礎(chǔ)部分一定要熟記和靈活運(yùn)用,足以應(yīng)付常見的頁面數(shù)據(jù)提取。下面就進(jìn)入實(shí)戰(zhàn)環(huán)節(jié),使用 Python 來實(shí)操 xpath 路徑表達(dá)式。

2. xpath 解析實(shí)戰(zhàn)

lxml 是 Python 中的一個(gè)解析庫,支持 HTML 和 XML 的解析,支持 XPath 解析方式,而且解析效率非常高。本節(jié)將安裝該模塊解析 html 文本并提取相應(yīng)的數(shù)據(jù)。

[store@server2 ~]$ sudo pip3 install lxml
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting lxml
  Downloading http://mirrors.cloud.aliyuncs.com/pypi/packages/55/6f/c87dffdd88a54dd26a3a9fef1d14b6384a9933c455c54ce3ca7d64a84c88/lxml-4.5.1-cp36-cp36m-manylinux1_x86_64.whl (5.5MB)
    100% |████████████████████████████████| 5.5MB 82.9MB/s 
Installing collected packages: lxml
Successfully installed lxml-4.5.1

我們先準(zhǔn)備好素材,也就是要解析的 HTML 文檔。為了更有代入感,我直接使用慕課網(wǎng) wiki 頁面的數(shù)據(jù)進(jìn)行操作,獲取數(shù)據(jù)的方式如下圖所示:
圖片描述

獲取慕課網(wǎng) wiki 頁面的 HTML 數(shù)據(jù)

最后保存到一個(gè) test.html 文本,然后我們要準(zhǔn)備一段 Python 代碼:

from lxml import etree

tree = etree.parse('test.html', etree.HTMLParser(encoding='utf8'))

def print_result(exp, results):
    print('xpath表達(dá)式為:{},其匹配結(jié)果為:'.format(exp))
    for res in results:
        print(res.strip())
    print('')

def test_xpath_expression(exp):
    results = tree.xpath(exp)
    print_result(exp, results)

將這個(gè) Python 文件命名為 test_xpath.py 和 test.html 放在同一級(jí)目錄下:

[store@server2 ~]$ ls
shen  test.html  test_xpath.py

接下來我們就可以進(jìn)行激動(dòng)人心的測試了,來完成一個(gè)簡單的實(shí)驗(yàn):

圖片描述

慕課網(wǎng) wiki 頁面數(shù)據(jù)獲取

第一個(gè)實(shí)驗(yàn)的目標(biāo)就是拿到 javascript 分類下的教程的三個(gè)數(shù)據(jù):標(biāo)題、總節(jié)數(shù)以及訪問次數(shù)。通過 F12 查看相關(guān)的 HTML 結(jié)構(gòu),我們可以通過如下的 Xpath表達(dá)式獲取相應(yīng)的數(shù)據(jù):

Python 3.6.8 (default, Apr  2 2020, 13:34:55) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from test_xpath import test_xpath_expression
>>> exp1 = '//h2[@class="language-title"]/text()'
>>> test_xpath_expression(exp1)
xpath表達(dá)式為://h2[@class="language-title"]/text(),其匹配結(jié)果為:
JavaScript
HTML & CSS
服務(wù)器
開發(fā)工具
其他后端語言
基礎(chǔ)應(yīng)用
框架應(yīng)用
基礎(chǔ)應(yīng)用
Python Web 開發(fā)
MySQL

接下來看一看元素的結(jié)構(gòu):

圖片描述

javascript 專欄的節(jié)點(diǎn)結(jié)構(gòu)

可以看到 javascript 專欄標(biāo)題是 h2 節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)同級(jí)下有一個(gè) div,它下面的四個(gè) div 節(jié)點(diǎn)正是那四個(gè)專欄。我們首先匹配下這四個(gè)專欄元素:

>>> exp1 = '//h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"]'
>>> test_xpath_expression(exp1)
xpath表達(dá)式為://h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"],其匹配結(jié)果為:
<Element div at 0x7f7015bf8808>
<Element div at 0x7f700c656788>
<Element div at 0x7f700c6567c8>
<Element div at 0x7f700c656808>

那么我們來進(jìn)一步分析每個(gè) div 內(nèi)部如何得到教程標(biāo)題、總節(jié)數(shù)以及訪問次數(shù)這些數(shù)據(jù):

圖片描述

獲取教程數(shù)據(jù)

可以看到,在前面找到 div 節(jié)點(diǎn)的基礎(chǔ)上在往下兩層,找到 class 屬性值為 text 的 div 節(jié)點(diǎn),所有的數(shù)據(jù)都在這個(gè)節(jié)點(diǎn)中:

  • 標(biāo)題:上面找到的 div 節(jié)點(diǎn)下的第一個(gè) a 節(jié)點(diǎn)的文本值;
  • 教程總節(jié)數(shù):上面找到的 div 節(jié)點(diǎn)下的第一個(gè) p 節(jié)點(diǎn)下第一個(gè) span 元素的文本值;
  • 總訪問次數(shù):上面找到的 div 節(jié)點(diǎn)下的第一個(gè) p 節(jié)點(diǎn)下第二個(gè) span 元素的文本值;

這樣我們就能進(jìn)行寫出提取相應(yīng)數(shù)據(jù)的 Xpath 路徑表達(dá)式了,測試如下:

>>> exp1 = '//h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"]/child::div/div[@class="text"]/a[1]/text()'
>>> test_xpath_expression(exp1)
xpath表達(dá)式為://h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"]/child::div/div[@class="text"]/a[1]/text(),其匹配結(jié)果為:
Javascript 入門教程
TypeScript 入門教程
Vue 入門教程
Ajax 入門教程

>>> exp2 = '//h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"]/child::div/div[@class="text"]/p/span[1]/text()'
>>> test_xpath_expression(exp2)
xpath表達(dá)式為://h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"]/child::div/div[@class="text"]/p/span[1]/text(),其匹配結(jié)果為:
56小節(jié)
38小節(jié)
39小節(jié)
9小節(jié)

>>> exp3 = '//h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"]/child::div/div[@class="text"]/p/span[2]/text()'
>>> test_xpath_expression(exp3)
xpath表達(dá)式為://h2[contains(text(), "JavaScript")]/following-sibling::div/div[@class="course-card"]/child::div/div[@class="text"]/p/span[2]/text(),其匹配結(jié)果為:
9832
3547
3628
1800

接下來我們整理下 Python 代碼,將整個(gè) wiki 頁面上的教程都解析出來,并將數(shù)據(jù)整理成 json 格式。預(yù)期最后的結(jié)果應(yīng)該是這樣的:

{
    '前端開發(fā)': {
        'JavaScript': [
            {'title': 'JavaScript入門教程', 'total_chapters': 56, 'total_visited': 9001},
            {...},
            {...},
            {...}
        ],
        'HTML & CSS': [ ... ]
    }
    '服務(wù)端相關(guān)': {
    
    },
    ...
}

這樣的難度再次增加,其核心的獲取數(shù)據(jù)的過程和上面一致。后面獲取其他數(shù)據(jù)的結(jié)果過程不作分析,大家有興趣仔細(xì)研究下代碼,然后動(dòng)手實(shí)操。話不多說,上代碼:

# 代碼文件:test_xpath2.py

from lxml import etree
def get_direction_data(direction_tree):
    """
    獲取一個(gè)方向下的課程數(shù)據(jù)
    :return:
    """
    direction_data = {}
    cards = direction_tree.xpath('.//div[@class="language-card"]')
    for card in cards:
        title = card.xpath('.//h2[@class="language-title"]/text()')[0]
        course_list = card.xpath('.//div[@class="course-card"]')
        courses = []
        for course in course_list:
            course_title = course.xpath('.//div[@class="text"]/a[1]/text()')[0]
            course_total_chaps = course.xpath('.//div[@class="text"]/p/span[1]/text()')[0]
            course_total_visit_count = course.xpath('.//div[@class="text"]/p/span[2]/text()')[0]
            courses.append({
                'course_title': course_title.strip(),
                'course_total_chaps': course_total_chaps.strip(),
                'course_total_visit_count': int(course_total_visit_count.strip())
            })
        direction_data[title] = courses
    return direction_data


def get_all_data():
    """
    解析慕課網(wǎng)wiki數(shù)據(jù)
    :return:
    """
    result = {}
    html = etree.parse('test.html', etree.HTMLParser(encoding='utf8'))
    directions = html.xpath('//div[@class="direction-con"]')
    for direction in directions:
        # 提取方向key,注意一定要有點(diǎn)號(hào),表示從當(dāng)前元素開始提取
        direction_name = direction.xpath('./div[@class="title-con"][1]/text()')
        if direction_name:
            result[direction_name[0]] = get_direction_data(direction)
    return result

運(yùn)行的結(jié)果如下:

[store@server2 ~]$ python3
Python 3.6.8 (default, Apr  2 2020, 13:34:55) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from test_xpath2 import get_all_dat
>>> get_all_data()
{'前端開發(fā)': {'JavaScript': [{'course_title': 'Javascript 入門教程', 'course_total_chaps': '56小節(jié)', 'course_total_visit_count': 9832}, {'course_title': 'TypeScript 入門教程', 'course_total_chaps': '38小節(jié)', 'course_total_visit_count': 3547}, {'course_title': 'Vue 入門教程', 'course_total_chaps': '39小節(jié)', 'course_total_visit_count': 3628}, {'course_title': 'Ajax 入門教程', 'course_total_chaps': '9小節(jié)', 'course_total_visit_count': 1800}], 'HTML & CSS': [{'course_title': 'CSS3 入門教程', 'course_total_chaps': '32小節(jié)', 'course_total_visit_count': 1512}, {'course_title': 'Less 入門教程', 'course_total_chaps': '22小節(jié)', 'course_total_visit_count': 364}, {'course_title': '雪碧圖入門教程', 'course_total_chaps': '24小節(jié)', 'course_total_visit_count': 915}]}, '服務(wù)端相關(guān)': {'服務(wù)器': [{'course_title': 'Nginx 入門教程', 'course_total_chaps': '24小節(jié)', 'course_total_visit_count': 4500}, {'course_title': 'HTTP 入門教程', 'course_total_chaps': '16小節(jié)', 'course_total_visit_count': 456}, {'course_title': 'Docker 入門教程', 'course_total_chaps': '25小節(jié)', 'course_total_visit_count': 1067}, {'course_title': 'Shell 入門教程', 'course_total_chaps': '17小節(jié)', 'course_total_visit_count': 2060}, {'course_title': 'Linux 入門教程', 'course_total_chaps': '25小節(jié)', 'course_total_visit_count': 1430}], '開發(fā)工具': [{'course_title': 'Gradle 入門教程', 'course_total_chaps': '12小節(jié)', 'course_total_visit_count': 1121}, {'course_title': 'Vim 入門教程', 'course_total_chaps': '14小節(jié)', 'course_total_visit_count': 1491}, {'course_title': 'RESTful 規(guī)范教程', 'course_total_chaps': '13小節(jié)', 'course_total_visit_count': 1316}, {'course_title': 'Markdown 入門教程', 'course_total_chaps': '31小節(jié)', 'course_total_visit_count': 733}, {'course_title': 'Maven 入門教程', 'course_total_chaps': '17小節(jié)', 'course_total_visit_count': 155}, {'course_title': 'GitHub 入門教程', 'course_total_chaps': '9小節(jié)', 'course_total_visit_count': 261}], '其他后端語言': [{'course_title': 'C 語言入門教程', 'course_total_chaps': '45小節(jié)', 'course_total_visit_count': 1933}, {'course_title': 'Go 入門教程', 'course_total_chaps': '36小節(jié)', 'course_total_visit_count': 691}, {'course_title': 'Ruby 入門教程', 'course_total_chaps': '26小節(jié)', 'course_total_visit_count': 410}]}, 'Java': {'基礎(chǔ)應(yīng)用': [{'course_title': 'Java 入門教程', 'course_total_chaps': '39小節(jié)', 'course_total_visit_count': 5229}, {'course_title': 'Android 入門教程', 'course_total_chaps': '29小節(jié)', 'course_total_visit_count': 553}, {'course_title': '算法入門教程', 'course_total_chaps': '11小節(jié)', 'course_total_visit_count': 628}], '框架應(yīng)用': [{'course_title': 'Spring Boot 入門教程', 'course_total_chaps': '25小節(jié)', 'course_total_visit_count': 4861}, {'course_title': 'Spring 入門教程', 'course_total_chaps': '21小節(jié)', 'course_total_visit_count': 850}, {'course_title': 'Hibernate 入門教程', 'course_total_chaps': '23小節(jié)', 'course_total_visit_count': 619}, {'course_title': 'MyBatis 入門教程', 'course_total_chaps': '23小節(jié)', 'course_total_visit_count': 895}]}, 'Python': {'基礎(chǔ)應(yīng)用': [{'course_title': 'Python 入門語法教程', 'course_total_chaps': '24小節(jié)', 'course_total_visit_count': 3617}, {'course_title': 'Python 原生爬蟲教程', 'course_total_chaps': '19小節(jié)', 'course_total_visit_count': 2001}, {'course_title': 'Python 進(jìn)階應(yīng)用教程', 'course_total_chaps': '29小節(jié)', 'course_total_visit_count': 726}], 'Python Web 開發(fā)': [{'course_title': 'Django 入門教程', 'course_total_chaps': '33小節(jié)', 'course_total_visit_count': 668}, {'course_title': 'NumPy 入門教程', 'course_total_chaps': '21小節(jié)', 'course_total_visit_count': 152}]}, '數(shù)據(jù)庫': {'MySQL': [{'course_title': 'MySQL 入門教程', 'course_total_chaps': '32小節(jié)', 'course_total_visit_count': 3638}, {'course_title': 'SQL 入門教程', 'course_total_chaps': '47小節(jié)', 'course_total_visit_count': 2406}]}}

是不是實(shí)現(xiàn)了預(yù)期效果?爬取網(wǎng)頁,解析數(shù)據(jù)的過程和這個(gè)類似。掌握好今天的內(nèi)容,你就已經(jīng)掌握了爬蟲的一個(gè)核心步驟。

3. 小結(jié)

本小節(jié)中,我們重點(diǎn)介紹了 Xpath 選擇器的一些基本知識(shí),包括通用的路徑表達(dá)式規(guī)則、運(yùn)輸符、軸的概念以及 Xpath 選擇器中常用的輔助函數(shù)。接下來我們用一段 Html 文本結(jié)合 Python 代碼進(jìn)行了實(shí)戰(zhàn)演示,幫助我們更好的理解 xpath 選擇器,本節(jié)課程就到這里,希望大家有所收獲。

圖片描述