乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      正則表達(dá)式學(xué)廢了?xpath來(lái)救!

       昵稱(chēng)73595512 2021-02-18

      使用XPath

      XPath,全稱(chēng)XML Path Language,即XML路徑語(yǔ)言,它是在XML語(yǔ)言中查找信息的語(yǔ)言。它最初是用來(lái)搜尋XML文檔的,但是它同樣適用于HTML文檔的搜索。

      在上一篇文章中講述了正則表達(dá)式的使用方法,正則表達(dá)式的難度還是比較大的,如果不花足夠多的時(shí)間去做的話(huà)還是比較難的,所以今天就來(lái)分享比正則簡(jiǎn)單的內(nèi)容,方便大家接下來(lái)的學(xué)習(xí)。

      XPath常用規(guī)則

      XPath的規(guī)則是非常豐富的,本篇文章無(wú)法一次性全部概括,只能為大家介紹幾個(gè)常用的規(guī)則。

      表達(dá)式描述
      nodename選取此節(jié)點(diǎn)的所有子節(jié)點(diǎn)
      /從當(dāng)前節(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)
      @選取屬性

      準(zhǔn)備工作

      在使用之前得先安裝好lxml這個(gè)庫(kù),如果沒(méi)有安裝請(qǐng)參考下面的安裝方式。

      pip install lxml

      案例導(dǎo)入

      現(xiàn)在通過(guò)實(shí)例來(lái)xpath對(duì)網(wǎng)頁(yè)解析的過(guò)程

      from lxml import etree


      text = '''
      <div>
          <ul>
              <li class="item-0"><a href="link1.html">first-item</a></li>
              <li class="item-1"><a href="link2.html"></a>second-item</li>
              <li class="item-inactive"><a href="link3.html">third-item</a></li>
              <li class="item-1"><a href="link4.html">fourth-item</a></li>
              <li class="item-0"><a href="link5.html">fifith-item</a></li>


      </div>
      '''

      html = etree.HTML(text)
      result = etree.tostring(html)
      print(result.decode('utf-8'))

      這里首先通過(guò)lxml這個(gè)庫(kù)導(dǎo)入etree這個(gè)模塊,然后聲明一段HTML文本,調(diào)用HTML類(lèi)進(jìn)行初始化,這就成功構(gòu)造了xpath對(duì)象。

      細(xì)心的讀者朋友應(yīng)該會(huì)發(fā)現(xiàn)我上面的代碼片段中標(biāo)簽ul是沒(méi)有閉合的,但是運(yùn)行之后你會(huì)發(fā)現(xiàn)運(yùn)行結(jié)果是閉合的,并且還自動(dòng)添加了htmlbody標(biāo)簽。

      這是因?yàn)槲覀冋{(diào)用了tostring( )方法幫助我們將HTML文本進(jìn)行修正,但是要注意的是tostring( )方法返回的結(jié)果是byte類(lèi)型,因此這里調(diào)用了tostring( )方法即可輸出修正后的HTML代碼。使用decode( )方法可以將byte類(lèi)型的數(shù)據(jù)轉(zhuǎn)成str類(lèi)型的數(shù)據(jù)。

      當(dāng)然,etree這個(gè)模塊也可以直接讀取文本文件進(jìn)行解析,具體代碼如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = etree.tostring(html)
      print(result.decode('utf-8'))

      其中文件test.html的內(nèi)容就是上面示例的HTML代碼。

      獲取所有的節(jié)點(diǎn)

      我們一般會(huì)使用 // 開(kāi)頭的Xpath規(guī)則來(lái)選取所有符合要求的節(jié)點(diǎn),假如我需要獲取所有的節(jié)點(diǎn),示例代碼如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//*')
      print(result)

      首先對(duì)上面的代碼做簡(jiǎn)單的說(shuō)明,這里的 * 代表匹配全部,所以所有的節(jié)點(diǎn)都會(huì)獲取到,返回值是一個(gè)列表。

      每個(gè)元素是Element類(lèi)型,其中后面跟的就是節(jié)點(diǎn)的名稱(chēng)。

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

      [<Element html at 0x1a0334c39c0>, <Element body at 0x1a0334c3a80>, <Element div at 0x1a0334c3ac0>, <Element ul at 0x1a0334c3b00>, <Element li at 0x1a0334c3b40>, <Element a at 0x1a0334c3bc0>, <Element li at 0x1a0334c3c00>, <Element a at 0x1a0334c3c40>, <Element li at 0x1a0334c3c80>, <Element a at 0x1a0334c3b80>, <Element li at 0x1a0334c3cc0>, <Element a at 0x1a0334c3d00>, <Element li at 0x1a0334c3d40>, <Element a at 0x1a0334c3d80>]

      從上面的運(yùn)行結(jié)果你會(huì)發(fā)現(xiàn),html、body、div、ul、li等等節(jié)點(diǎn)。

      獲取指定節(jié)點(diǎn)

      例如,在這里我要獲取到所有的li節(jié)點(diǎn),那該怎么辦呢?其實(shí)很簡(jiǎn)單,具體代碼示例如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//li')
      print(result)

      通過(guò)上面的幾個(gè)例子,不知道大家有沒(méi)有明白節(jié)點(diǎn)的含義。

      其實(shí)節(jié)點(diǎn)的含義你可以理解為當(dāng)前的html文檔開(kāi)始的地方。

      如果上面的代碼你修改一段,變成這樣:

      result = html.xpath('/li')

      運(yùn)行之后你會(huì)發(fā)現(xiàn)列表是空的,因?yàn)樵撐臋n的的子節(jié)點(diǎn)中沒(méi)有 li 這個(gè)節(jié)點(diǎn),li 是該文檔的子孫節(jié)點(diǎn),而該文檔的子節(jié)點(diǎn)是html。

      所以,你將代碼這樣修改:

      result = html.xpath('/html')
      # 另一種寫(xiě)法
      result = html.xpath('.')

      運(yùn)行之后你會(huì)驚喜的發(fā)現(xiàn),成功獲取到了html節(jié)點(diǎn)。

      子節(jié)點(diǎn)與子孫節(jié)點(diǎn)

      通過(guò)/或//即可查好元素的子節(jié)點(diǎn)或者是子孫節(jié)點(diǎn),假如你想要選擇 li 節(jié)點(diǎn)下的所有 a 節(jié)點(diǎn)可以這樣實(shí)現(xiàn),具體代碼如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//li/a')
      print(result)

      先對(duì)上面的代碼做簡(jiǎn)要的說(shuō)明://li表示獲取所有的li節(jié)點(diǎn),/a表示獲取 li 節(jié)點(diǎn)下的子節(jié)點(diǎn) a 。

      或者也可以這樣寫(xiě),你可以先獲取到所有的 ul 節(jié)點(diǎn),再獲取 ul 節(jié)點(diǎn)下的所有子孫節(jié)點(diǎn) a 節(jié)點(diǎn)。

      具體示例代碼如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//ul//a')    # 注意//a
      print(result)

      運(yùn)行上面的代碼你會(huì)發(fā)現(xiàn)結(jié)果是相同的。

      獲取父節(jié)點(diǎn)

      通過(guò)上面的幾個(gè)例子,想必應(yīng)該知道何為子節(jié)點(diǎn)與子孫節(jié)點(diǎn)。那么如何尋找父節(jié)點(diǎn)呢?這里可以通過(guò) .. 來(lái)實(shí)現(xiàn)。

      比如,我現(xiàn)在要選中href屬性為link4.html的a節(jié)點(diǎn),然后再獲取其父節(jié)點(diǎn),再獲取其class屬性??粗鴥?nèi)容好多,那就要一個(gè)一個(gè)來(lái),不要著急。

      具體代碼示例如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//a[@href="link4.html"]/../@class')
      print(result)

      運(yùn)行結(jié)果

      ['item-1']

      屬性的匹配

      在選取數(shù)據(jù)的時(shí)候,可以使用@符號(hào)進(jìn)行屬性的過(guò)濾,比如:這里通過(guò)選取 li 標(biāo)簽屬性class為item-0的節(jié)點(diǎn),可以這樣實(shí)現(xiàn):

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//li[@class="item-0"]')
      print(result)

      你可以試著運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn)匹配到了兩個(gè)正確的結(jié)果。

      文本獲取

      在整個(gè)HTML文檔中肯定會(huì)有很多的文本內(nèi)容,有些恰恰是我們需要的,那么應(yīng)該如何獲取這些文本內(nèi)容呢?

      接下來(lái)可以嘗試使用text( )方法獲取節(jié)點(diǎn)中的文本。

      具體代碼實(shí)例如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//li[@class="item-0"]/a/text()')
      print(result)

      試著運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn),已經(jīng)獲取到了所有class屬性為item-0的 li 節(jié)點(diǎn)下的文本。

      獲取標(biāo)簽屬性值

      在編寫(xiě)爬蟲(chóng)的過(guò)程中,很多時(shí)候我們需要的數(shù)據(jù)可能是屬性值,那就要學(xué)會(huì)如何來(lái)獲取我們想要的屬性值了。

      例如,我想要獲取 li 節(jié)點(diǎn)下的a節(jié)點(diǎn)的所有href屬性,具體代碼示例如下所示:

      from lxml import etree


      html = etree.parse('./test.html', etree.HTMLParser())
      result = html.xpath('//li/a/@href')
      print(result)

      通過(guò)@href就獲取到了該節(jié)點(diǎn)的href屬性值,當(dāng)然,它們都是以列表的形式返回。

      屬性多值的匹配

      在編寫(xiě)前端代碼的時(shí)候,有些節(jié)點(diǎn)為了方便可能就會(huì)存在多個(gè)值,那么就要使用contains函數(shù)了,例如:

      from lxml import etree

      text = '''
      <li class="li li-first"><a href="link.html">first item</a></li>

      '''

      html = etree.HTML(text)
      result = html.xpath('//li[contains(@class, "li")]/a/text()')
      print(result)

      要是你說(shuō)我怎么記得住這些函數(shù),那好,還可以這樣寫(xiě)。

      具體代碼示例如下:

      from lxml import etree

      text = '''
      <li class="li li-first"><a href="link.html">first item</a></li>

      '''

      html = etree.HTML(text)
      result = html.xpath('//li[@class="li li-first"]/a/text()')
      print(result)

      看出區(qū)別了嗎?

      運(yùn)行上面的兩段代碼,你會(huì)發(fā)現(xiàn)結(jié)果是一樣的。

      多屬性匹配

      另外,我們寫(xiě)寫(xiě)爬蟲(chóng)的時(shí)候會(huì)遇到另一種情況,那就是在一個(gè)標(biāo)簽內(nèi)存在多個(gè)屬性。那此時(shí)可以用and操作符來(lái)連接

      具體代碼示例如下所示:

      from lxml import etree

      text = '''
      <li class="li li-first" name="item"><a href="link.html">first item</a></li>

      '''

      html = etree.HTML(text)
      result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
      print(result)

      xpath運(yùn)算符的簡(jiǎn)單介紹

      從上面的示例你應(yīng)該知道了,and是xpath的運(yùn)算符,xpath的運(yùn)算符也是比較多的,那么接下來(lái)對(duì)xpath運(yùn)算符做簡(jiǎn)單的介紹。

      運(yùn)算符描述
      or
      and
      |計(jì)算兩個(gè)節(jié)點(diǎn)集,//li | //a 獲取li和a元素的節(jié)點(diǎn)集
      +加法
      -減法
      *乘法
      div除法
      =等于
      !=不等于
      <小于
      >大于
      >=大于等于
      <=小于等于
      mod計(jì)算余數(shù)

      按序選擇

      有時(shí)候,我們編寫(xiě)爬蟲(chóng)的時(shí)候可能會(huì)匹配到幾個(gè)相同的 li 節(jié)點(diǎn),但是,我只需要第一個(gè)或者最后一個(gè)就可以了。那這時(shí)該怎么樣處理那?

      這時(shí)可以通過(guò)索引的方式,傳入指定的索引,獲取指定節(jié)點(diǎn)。

      具體代碼示例如下所示:

      from lxml import etree

      text = '''
      <div>
          <ul>
              <li class="item-0"><a href="link1.html">first-item</a></li>
              <li class="item-1"><a href="link2.html"></a>second-item</li>
              <li class="item-inactive"><a href="link3.html">third-item</a></li>
              <li class="item-1"><a href="link4.html">fourth-item</a></li>
              <li class="item-0"><a href="link5.html">fifith-item</a></li>
          </ul>

      </div>
      '''

      html = etree.HTML(text)
      # 獲取第一個(gè)li節(jié)點(diǎn)
      result = html.xpath('//li[1]/a/text()')
      print(result)
      # 獲取最后一個(gè)li節(jié)點(diǎn)
      result = html.xpath('//li[last()]/a/text()')
      print(result)
      # 獲取位置小于3的li節(jié)點(diǎn)
      result = html.xpath('//li[position()<3]/a/text()')
      print(result)
      # 獲取倒數(shù)第三個(gè)li節(jié)點(diǎn)
      result = html.xpath('//li[last()-2]/a/text()')
      print(result)

      上述內(nèi)容所描述的是xpath的在爬蟲(chóng)應(yīng)用中常見(jiàn)使用方法,如果各位小伙伴想要繼續(xù)深入學(xué)習(xí)的話(huà),可以參考w3c進(jìn)行學(xué)習(xí),網(wǎng)址如下:

      https://www.w3school.com.cn/xpath/xpath_syntax.asp

      實(shí)戰(zhàn)

      上面的內(nèi)容是描述xpath的使用語(yǔ)法,建議大家要花一個(gè)小時(shí)左右的時(shí)間去練習(xí)。

      那么接下來(lái)就帶大家進(jìn)入實(shí)戰(zhàn)演練了,乘熱打鐵是最好的學(xué)習(xí)方式。

      今天我?guī)?lái)的內(nèi)容就是爬取必應(yīng)壁紙。

      準(zhǔn)備工作

      工欲善其事,必利其器。玩爬蟲(chóng)也是同樣的道理。

      首先,安裝好兩個(gè)庫(kù):lxml與requests。

      pip install lxml
      pip install requests

      需求分析

      爬取的網(wǎng)址:https://bing./

      抓包分析

      首先打開(kāi)開(kāi)發(fā)者工具,隨便點(diǎn)擊一張圖片進(jìn)入它的高清大圖,點(diǎn)擊network進(jìn)行抓包,在點(diǎn)擊圖片的下載按鈕。

      點(diǎn)擊下載按鈕之后,你會(huì)發(fā)現(xiàn),瀏覽器向圖中的網(wǎng)址發(fā)起了請(qǐng)求,點(diǎn)擊進(jìn)去之后發(fā)現(xiàn)這個(gè)就是高清圖片的鏈接地址。

      從而我們的第一個(gè)需求就是獲取所有圖片的鏈接地址。

      獲取圖片鏈接

      為什么要獲取圖片鏈接呢?

      首先,你思考一下,每一張圖片你都要點(diǎn)擊下載按鈕來(lái)將圖片保存到本地嗎?如果你不懂爬蟲(chóng)那當(dāng)然沒(méi)有辦法了。但是,我們懂爬蟲(chóng)的人還會(huì)這么干嗎?

      既然每一次點(diǎn)擊下載按鈕,瀏覽器都是向?qū)?yīng)的高清大圖發(fā)起請(qǐng)求,那么也就是說(shuō)我們可以獲取到所有的圖片鏈接,然后利用Python模擬瀏覽器向這些鏈接發(fā)起請(qǐng)求,即可下載這些圖片。

      鏈接如下:

      https://h2./bing/LoonyDook_ZH-CN1879420705_1920x1080.jpg?imageslim

      關(guān)于翻頁(yè)

      打開(kāi)網(wǎng)頁(yè)之后,你會(huì)發(fā)現(xiàn)起碼有100頁(yè)的圖片。那這100頁(yè)的圖片怎么樣獲取呢?

      很簡(jiǎn)單,依然還是先分析每一頁(yè)的URL地址,看看有沒(méi)什么變化規(guī)律。

      # 第二頁(yè)
      https://bing./?p=2
      # 第三頁(yè)
      https://bing./?p=3

      其實(shí)看到上面的URL變化之后,我想你也應(yīng)該明白了變化的規(guī)律了吧。

      功能實(shí)現(xiàn)

      構(gòu)造每一頁(yè)的鏈接

      其實(shí)就是實(shí)現(xiàn)簡(jiǎn)單的翻頁(yè)功能。

      具體代碼示例如下所示:

      def get_page_url():
          page_url = []
          for i in range(1,148):
              url = f'https://bing./?p={i}'
              page_url.append(url)
          return page_url

      上面代碼的功能是構(gòu)造每一頁(yè)的鏈接。將鏈接保存在page_url中。

      獲取每一頁(yè)中的圖片鏈接

      在上圖中你會(huì)發(fā)現(xiàn),圖片的鏈接就藏在了data-progressive里面,這不就是img標(biāo)簽的屬性嗎?有何難?

      但是細(xì)心的朋友就會(huì)發(fā)現(xiàn),這個(gè)鏈接和我們最開(kāi)始抓包的鏈接是不一樣的,到底哪里不一樣呢?

      我們來(lái)具體看看

      https://h2./bing/LoonyDook_ZH-CN1879420705_1920x1080.jpg?imageslim
      http://h2./bing/LoonyDook_ZH-CN1879420705_640x480.jpg?imageslim

      發(fā)現(xiàn)了嗎?分辨率是不一樣的。其他都相同的,那只要將分辨率替換掉就可以了呀。

      具體代碼如下所示:

      def get_img_url(urls):
          headers = {
              'User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
          }
          img_urls = []
          count = 1
          for url in urls[:3]:
              time.sleep(5)
              text = requests.get(url, headers=headers).content.decode('utf-8')
              html = etree.HTML(text)
              img_url = html.xpath('//div[@class="item"]//img/@data-progressive')
              img_url = [i.replace('640x480''1920x1080'for i in img_url]
              print(f'正在獲取第{count}頁(yè)鏈接')

              img_urls.extend(img_url)
              count += 1
          return img_urls

      上面的代碼是獲取每一頁(yè)的圖片鏈接,將鏈接保存在img_urls中。

      保存圖片

       def save_img(self, img_urls):

              headers = {
                  'User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
              }
              count = 1

              for img_url in img_urls:
                  content = requests.get(img_url, headers=headers).content
                  print(f'正在下載第{count}張')
                  with open(f'../image/{count}.jpg''wb'as f:
                      f.write(content)
                  count += 1

      保存圖片的代碼還是比較簡(jiǎn)單的,可以將獲取到的所有圖片鏈接作為參數(shù)傳進(jìn)來(lái),進(jìn)行逐個(gè)訪(fǎng)問(wèn),即可。

      文章來(lái)源:https://www./blog-lyU3E5QA6g.htm

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多