xpath模塊使用


xpath模塊使用

一、什么是xml(百度百科解釋如下)

可擴展標記語言,標准通用標記語言的子集,簡稱XML。是一種用於標記電子文件使其具有結構性的標記語言。
在電子計算機中,標記指計算機所能理解的信息符號,通過此種標記,計算機之間可以處理包含各種的信息比如文章等。它可以用來標記數據、定義數據類型,是一種允許用戶對自己的標記語言進行定義的源語言。 它非常適合萬維網傳輸,提供統一的方法來描述和交換獨立於應用程序或供應商的結構化數據。是Internet環境中跨平台的、依賴於內容的技術,也是當今處理分布式結構信息的有效工具。早在1998年,W3C就發布了XML1.0規范,使用它來簡化Internet的文檔信息傳輸。

1.1、總結如下

​ 1、定義:可擴展標記性語言(EXtensible Markup Language)
​ 2、特定:xml是具有自描述特性的半結構化數據。
​ 3、作用:xml主要用來傳輸數據

二、xml與html的區別

  • 語法嚴格性不同
    • 在html中不區分大小寫,在xml中嚴格區分。
    • 在html中,如果你可以省略某些結尾標簽,還是可以正常顯示。在xml中,是嚴格的樹狀結構,絕對不能省略任何標記。
    • 在xml中,沒有結尾標簽的標簽,必須用一個/字符作為結尾。<a href='www'/>
    • 在xml中,屬性值必須使用在引號。在html中,引號可用可不用。
    • 在html中屬性名可以不帶屬性值,xml必須帶。
    • xml文檔中,空白部分不會被解析器自動刪除,但是html是過濾掉空格的。
  • 標記不同
    • html使用固有的標記,xml沒有固有標記。
    • html標簽是預定義的,xml標簽是自定義的、可擴展的。
  • 用途不同
    • html的設計宗旨是用來顯示數據。
    • xml是用來傳輸數據的,或者用於配置文件。

三、xpath

3.1、什么是xpath
  • XPath 使用路徑表達式在 XML 文檔中進行導航
  • XPath 包含一個標准函數庫
  • XPath 是 XSLT 中的主要元素
  • XPath 是一個 W3C 標准

簡而言之,xpath是在xml文檔中,根據路徑查找元素的語法。

3.2、xpath常用術語
  • 元素:文檔樹中的標簽就是一個元素。
  • 節點:表示xml文檔樹的某一個位置,例如 / 代表根節點,代表文檔樹起始位置,元素也可以看成某一位置上的節點。
  • 屬性:<title lang="eng">Harry Potter</title>lang就是某一個節點的屬性。
  • 文本:<title lang="eng">Harry Potter</title>中Harry Potter就是文本。
3.3、xpath語法
<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>

<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>

</bookstore>
  • 選取節點

    • 表達式 描述
      nodename 選取標簽或者元素 。
      / 從根節點選取,或者代表下一個節點。
      // 從文檔中的任意位置。
      . 選取當前節點。
      .. 選取當前節點的父節點。
      @ 選取屬性.
  • 謂語,限定我們進行取值。

    • 通過位置進行限定

      • 路徑表達式 結果
        /bookstore/book[1] 選取屬於 bookstore 子元素的第一個 book 元素。
        /bookstore/book[last()] 選取屬於 bookstore 子元素的最后一個 book 元素。
        /bookstore/book[last()-1] 選取屬於 bookstore 子元素的倒數第二個 book 元素。
        /bookstore/book[position()<3] 選取最前面的兩個屬於 bookstore 元素的子元素的 book 元素。
    • 通過屬性和內容限定

      • 路徑表達式 結果
        //title[@lang] 選取所有擁有名為 lang 的屬性的 title 元素。
        //title[@lang="eng"] 選取所有 title 元素,且這些元素擁有值為 eng 的 lang 屬性。
        //title[contains(@lang,"eng")] 選取所有 title 元素,且這些元素包含值為 eng 的 lang 屬性。
        //title[not(contains(@lang,"eng"))] 選取所有 title 元素,且不包含元素屬性值為 eng 的 lang 屬性。
        /bookstore/book[price>35.00] 選取 bookstore 元素的所有 book 元素,且其中的 price 元素的值須大於 35.00。
        //title | //price 選取文檔中的所有 title 和 price 元素。
  • 選取位置節點

    • 通配符 描述 表達式 結果
      * 匹配任何元素節點。 //book/* 選取book節點下的所有元素
      @* 匹配任何屬性節點。 //title/@* 選取title節點所有屬性

四、lxml 模塊

lxml模塊是python中的處理xml文件的第三方庫.

pip install lxml
使用
from lxml import etree

xmls = '''
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>

<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>

<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>

</bookstore>
'''

xml = etree.HTML(xmls)
print(xml.xpath('//title[@lang="eng"]')) #選取所有 title 元素,且這些元素擁有值為 eng 的 lang 屬性。

#結果
[<Element title at 0x2a564beca48>, <Element title at 0x2a564beca88>]#我們查找元素或者節點時返回的是一個 Element 對象組成的列表

print(xml.xpath('//title[@lang="eng"]/text()')) #選取所有 title 元素的文本內容。

#結果
['Harry Potter', 'Learning XML']#我們查找元素或者節點的文本內容時返回的是一個字符串組成的列表

print(xml.xpath('//title[@lang="eng"]/@lang')) #選取所有 title 元素的屬性。

#結果
['eng', 'eng']#我們查找元素或者節點的屬性值時返回的是一個字符串組成的列表。

五、相關案例

5.1、扇貝單詞列表,我這里寫入了excel文件中
import requests, xlwt
from lxml import etree

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36'
}

con_list = []


def get_url():
    base_url = 'https://www.shanbay.com/wordlist/110521/232414/?page={}'
    return [base_url.format(i) for i in range(1, 4)]


def get_content(url):
    return requests.get(url, headers=headers).content.decode('utf-8')


def parse_content(content):
    html = etree.HTML(content)
    tr_list = html.xpath('//tr[@class="row"]')
    for word in tr_list:
        dic = {
            'words': word.xpath('.//strong/text()')[0],
            'mean': word.xpath('.//td[@class="span10"]/text()')[0]
        }
        con_list.append(dic)


def write_to_excel(filename, sheetname, word_list): #使用xlwt 模塊將數據寫入excel文件中
    # 創建workbook
    file = xlwt.Workbook()

    # 添加sheet表
    sheet = file.add_sheet(sheetname)

    # 設置表頭
    head = [i for i in word_list[0].keys()]
    for i in range(len(head)):
        sheet.write(0, i, head[i])
    # 寫內容
    i = 1
    for item in word_list:
        for j in range(len(head)):
            sheet.write(i, j, item[head[j]])
        i += 1
    # 保存
    file.save(filename)
    print('寫入excle成功!')


def main():
    for url in get_url():
        content = get_content(url)
        parse_content(content)
    write_to_excel('./1.xls', 'words', con_list)


if __name__ == "__main__":
    main()

執行結果

5.2、抓取網易雲所有歌手
import requests
from lxml import etree
from queue import Queue, Empty
from concurrent.futures import ThreadPoolExecutor
import json
import os


class WyySinger(object):
    def __init__(self, path='./'):
        self.start_url = 'https://music.163.com/discover/artist'
        self.basic_url = 'https://music.163.com'
        self.headers = {
            'referer': 'https://music.163.com/',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36',
        }
        self.url_list = []
        self.queue = Queue()
        self.path = path

    def get_html(self, url):
        res = requests.get(url, headers=self.headers)
        return res.text

    def get_url_list(self, html, xpath_exp):
        html = etree.HTML(html)
        url_class_list = html.xpath(xpath_exp)
        return [self.basic_url + i for i in url_class_list]

    def get_content(self, url):
        html = self.get_html(url)
        self.queue.put(html)

    def save_to_file(self, content):
        filename = os.path.join(self.path, 'singer.json')
        with open(filename, 'a', encoding='utf-8') as file:
            json.dump(content, file, ensure_ascii=False)
            file.write('\n')

    def parse_html(self, content):
        html = etree.HTML(content)
        a_list = html.xpath('//a[@class="nm nm-icn f-thide s-fc0"]')
        for i in a_list:
            name = i.xpath('./text()')[0]
            url = self.basic_url + i.xpath('./@href')[0].strip()
            item = {
                'url': url,
                'name': name
            }
            print(item)
            self.save_to_file(item)

    def run(self):
        start_content = self.get_html(self.start_url)
        start_exp = '//div[@class="blk"]/ul/li/a/@href'
        initial_exp = '//ul[@id="initial-selector"]/li[position()>1]/a/@href'
        url_list = self.get_url_list(start_content, start_exp)# 獲取大分類url列表
        for url in url_list:
            html = self.get_html(url)
            initial_url_list = self.get_url_list(html, initial_exp) #獲取大分類下的小分類url列表
            for url in initial_url_list:
                self.url_list.append(url)
        p_list = []
        pool = ThreadPoolExecutor(max_workers=20) #創建20個線程的線程池。
        for url in self.url_list:
            p = pool.submit(self.get_content, url) #多線程進行抓取,並且將抓取的網頁內容放入隊列中
            p_list.append(p)

        for th in p_list:
            th.result() #等待所有抓取網頁線程抓取完畢
        while True:
            try:
                content = self.queue.get_nowait() #從隊列中獲取內容,多線程解析內容,提取數據,並且將其寫入文件中。
                pool.submit(self.parse_html, content)
            except Empty as e:
                print(e) #報錯結束任務,跳出循環
                break

if __name__ == '__main__':
    singer = WyySinger()
    singer.run()

雖然抓取的是所有歌手,但是當我進行測試的時候,還是發現部分歌手沒抓取下來。

執行結果

5.3、抓取古詩文網所有詩文https://www.gushiwen.org,並且分類保存到word文檔中。
代碼如下:
import requests
from lxml import etree
import os
from queue import Queue, Empty
from concurrent.futures import ThreadPoolExecutor
import re
from docx import Document
from docx.oxml.ns import qn
from docx.shared import Pt
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.shared import Inches


class Gushiwen(object):
    def __init__(self, path='.'):
        self.base_url = 'https://www.gushiwen.org'
        self.start_url = 'https://so.gushiwen.org/shiwen/'
        self.headers = {

            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36',
        }
        self.path = os.path.abspath(path)
        self.queue = Queue()

    def get_html(self, url):
        res = requests.get(url, headers=self.headers)
        return res.text

    def get_url_list(self, html, xpath_exp):
        res_list = []
        html = etree.HTML(html)
        for exp in xpath_exp:
            url_class_list = html.xpath(exp)
            res_list.append(url_class_list)
        return zip(*res_list)

    def parse_content(self, info):
        html = etree.HTML(self.get_html(info[0]))
        try:
            title = html.xpath('//div[@class="sons"]/div[@class="cont"][1]/h1/text()')[0]
            damon = html.xpath('//div[@class="sons"]/div[@class="cont"][1]/p[1]/a[1]/text()')[0]
            author = html.xpath('//div[@class="sons"]/div[@class="cont"][1]/p[1]/a[2]/text()')[0]
            content = html.xpath('//div[@class="sons"]/div[@class="cont"][1]/div[@class="contson"]//text()')
            yiwen = html.xpath('//div[@class="contyishang"]/p[1]/text()')
            yiwen.pop()
            note = html.xpath('//div[@class="contyishang"]/p[2]/text()')
            note.pop()
            new_con = []
            for item in content:
                item = re.sub('\s+', '', item)
                if item:
                    new_con.append(item)
            dic = {
                'title': title,
                'damon': damon,
                'author': author,
                'content': new_con,
                'yiwen': yiwen,
                'note': note,
            }
            return [info[1], dic]
        except IndexError as e:
            return None

    def save_to_file(self, content):
        content = content.result()
        if content:
            print(content[1]['title'])
            # 聲明doc對象
            doc = Document()

            # 設置字體
            doc.styles['Normal'].font.name = u'宋體'
            doc.styles['Normal'].element.rPr.rFonts.set(qn('w:eastAsia'), u'宋體')

            # 添加文檔標題
            paragraph = doc.add_paragraph()
            run = paragraph.add_run(content[1]['title'])
            font = run.font

            # 設置字體大小
            font.size = Pt(15)

            # 設置字體居中
            paragraph_format = paragraph.paragraph_format
            paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

            # 添加作者朝代
            paragraph = doc.add_paragraph()
            run = paragraph.add_run('    ' + content[1]['damon'] + "  " + content[1]['author'])
            font = run.font
            font.size = Pt(10)
            paragraph_format = paragraph.paragraph_format
            paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            paragraph_format.first_line_indent = Inches(0.6)

            # 添加正文小標題
            paragraph = doc.add_paragraph()
            paragraph.add_run('正文:')
            paragraph_format = paragraph.paragraph_format
            # 段前
            paragraph_format.space_after = Pt(2)
            # 段后
            paragraph_format.space_before = Pt(2)

            # 添加正文內容
            paragraph = doc.add_paragraph(''.join(content[1]['content']))
            paragraph_format = paragraph.paragraph_format
            paragraph_format.first_line_indent = Inches(0.3)

            # 添加譯文小標題
            paragraph = doc.add_paragraph()
            paragraph.add_run('譯文:')
            paragraph_format = paragraph.paragraph_format
            paragraph_format.space_after = Pt(2)
            paragraph_format.space_before = Pt(2)

            # 添加譯文內容
            paragraph = doc.add_paragraph(''.join(content[1]['yiwen']))
            paragraph_format = paragraph.paragraph_format
            paragraph_format.first_line_indent = Inches(0.3)

            # 添加注釋小標題
            paragraph = doc.add_paragraph()
            paragraph.add_run('注釋:')
            paragraph_format = paragraph.paragraph_format
            paragraph_format.space_after = Pt(2)
            paragraph_format.space_before = Pt(2)

            # 添加注釋內容
            paragraph = doc.add_paragraph(''.join(content[1]['note']))
            paragraph_format = paragraph.paragraph_format
            paragraph_format.first_line_indent = Inches(0.3)

            # 保存內容
            os.chdir(content[0])
            try:
                doc.save('{}.docx'.format(content[1]['title']))
            except:
                title = re.sub(r' / ', '', content[1]['title'])
                doc.save('{}.docx'.format(title))

    def make_dir(self, dirname):
        path = os.path.join(self.path, dirname)
        if not os.path.exists(path):
            os.mkdir(path)
        return path

    def run(self):
        index_html = self.get_html(self.start_url)
        class_exp_list = ['//div[@class="sons"][1]/div[@class="cont"]/a/@href',
                          '//div[@class="sons"][1]/div[@class="cont"]/a/text()']
        content_exp_list = ['//div[@class="typecont"]/span/a/@href']
        class_url_list = self.get_url_list(index_html, class_exp_list)
        print('開始url抓取...'.center(50, '*'))
        for item in class_url_list:
            print('正在獲取分類url--->{}'.format(item[1]))
            path = self.make_dir(item[1])  # 分類文件夾路徑
            content_html = self.get_html(self.base_url + item[0])
            content_url_list = self.get_url_list(content_html, content_exp_list)
            for url in content_url_list:
                print('正在獲取詳情頁url--->{}'.format(url))
                self.queue.put((url[0], path))
        print('url抓取完成...'.center(50, '*'))
        pool = ThreadPoolExecutor(max_workers=50)
        print('開始數據寫入...'.center(50, '*'))
        while True:
            try:
                info = self.queue.get_nowait()
                p = pool.submit(self.parse_content, info)
                p.add_done_callback(self.save_to_file)
            except Empty as e:
                break
        pool.shutdown(wait=True)
        print('抓取完成...'.center(50, '*'))


if __name__ == '__main__':
    gsc = Gushiwen()
    gsc.run()
執行結果如下

具體wrod文檔顯示如下,不過當中還是會有一些格式錯誤。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM