爬蟲——網頁解析利器–re & xpath

  • 2019 年 10 月 3 日
  • 筆記

正則解析模塊re

re模塊使用流程

方法一

r_list=re.findall(‘正則表達式’,html,re.S)

方法二  創建正則編譯對象

pattern = re.compile(‘正則表達式’,re.S)
r_list = pattern.findall(html)

正則表達式元字符:https://www.cnblogs.com/LXP-Never/p/9522475.html

類別 元字符
匹配字符 . […] [^…] d D w W s S
匹配重複 * + ? {n} {m,n}
匹配位置 ^ $ A Z b B
其他 | ()

匹配任意一個字符的正則表達式

import re    pattern = re.compile('.',re.S)        # 方法一  pattern = re.compile('[sS]')        # 方法二 

貪婪匹配和非貪婪匹配

貪婪匹配(默認)

  1. 在整個表達式匹配成功的前提下,儘可能多的匹配 * + ?
  2. 表示方式: .* .+ .?

非貪婪匹配

  1. 在整個表達式匹配成功的前提下,儘可能少的匹配 * + ?
  2. 表示方式: .*? .+? .??

正則表達式分組

作用:在完整的模式中定義子模式,將每個圓括號中子模式匹配出來

import re    s = 'A B C D'  p1 = re.compile('w+s+w+')  print(p1.findall(s))        # # ['A B','C D']    p2 = re.compile('(w+)s+w+')  print(p2.findall(s))        # # ['A','C']    p3 = re.compile('(w+)s+(w+)')  print(p3.findall(s))        # # [('A','B'),('C','D')]

import re    html = '''<div class="animal">      <p class="name">          <a title="Tiger"></a>      </p>      <p class="content">          Two tigers two tigers run fast      </p>  </div>    <div class="animal">      <p class="name">          <a title="Rabbit"></a>      </p>        <p class="content">          Small white rabbit white and white      </p>  </div>'''    pattern = re.compile(      '<div class="animal">.*?title="(.*?)".*?'      'class="content">(.*?)</p>',      re.S)  r_list = pattern.findall(html)  print(r_list)

View Code

分組總結

  1. 在網頁中,想要什麼內容,就加 ( )
  2. 先按整體正則匹配,然後再提取分組()中的內容  
  3. 如果有2個及以上分組(),則結果中以元組形式顯示 [(),(),()]

xpath解析

XPath即為XML路徑語言,它是一種用來確定XML文檔中某部分位置的語言,同樣適用於HTML文檔的檢索,我們來利用xpath對HTML代碼進行檢索試試,以下是HTML示例代碼。

<ul class="book_list">      <li>          <title class="book_001">Harry Potter</title>          <author>J K. Rowling</author>          <year>2005</year>          <price>69.99</price>      </li><li>          <title class="book_002">Spider</title>          <author>Forever</author>          <year>2019</year>          <price>49.99</price>      </li>  </ul> 

匹配演示

1、查找所有的li節點

//li

2、查找li節點下的title子節點中,class屬性值為’book_001’的節點

//li/title[@class="book_001"]

3、查找li節點下所有title節點的,class屬性的值

//li//title/@class

只要涉及到條件,加 []
只要獲取屬性值,加 @

選取節點

// :從所有節點中查找(包括子節點和後代節點)

@  :獲取屬性值

# 使用場景1(屬性值作為條件)    //div[@class="movie"]    # 使用場景2(直接獲取屬性值)    //div/a/@src

匹配多路徑(或)

xpath表達式1 | xpath表達式2 | xpath表達式3

contains() :匹配屬性值中包含某些字符串節點

# 查找class屬性值中包含"book_"的title節點  //title[contains(@class,"book_")]    # 匹配ID名含qiushi_tag_字符串的div節點  //div[contains(@id,"qiushi_tag_")]

text() :獲取節點的文本內容

# 查找所有書籍的名稱  //ul[@class="book_list"]/li/title  # 結果:<element title at xxxx>  //ul[@class="book_list"]/li/title/text()  # 結果:'Harry Potter'

練習貓眼電影xpath信息檢索:https://maoyan.com/board/4?offset=1

1、獲取貓眼電影中電影信息的 dd 節點

//dl[@class="board-wrapper"]/dd

2、獲取電影名稱

//dl[@class="board-wrapper"]/dd//p[@class="name"]/a/text()

3、獲取電影主演的

//dl[@class="board-wrapper"]/dd//p[@class="star"]/text()

4、獲取上映商檢的xpath

//dl[@class="board-wrapper"]/dd//p[@class="releasetime"]/text()

xpath解析庫lxml

  1. 導模塊  from lxml import etree
  2. 創建解析對象  parse_html = etree.HTML(html)
  3. 解析對象調用xpath,只要調用xpath,結果一定為列表  r_list = parse_html.xpath(‘xpath表達式’)
from lxml import etree    html = """  <div class="wrapper">      <i class="iconfont icon-back" id="back"></i>      <a href="/" id="channel">新浪社會</a>      <ul id="nav">          <li><a href="http://domestic.firefox.sina.com/" title="國內">國內</a></li>          <li><a href="http://world.firefox.sina.com/" title="國際">國際</a></li>          <li><a href="http://mil.firefox.sina.com/" title="軍事">軍事</a></li>          <li><a href="http://photo.firefox.sina.com/" title="圖片">圖片</a></li>          <li><a href="http://society.firefox.sina.com/" title="社會">社會</a></li>          <li><a href="http://ent.firefox.sina.com/" title="娛樂">娛樂</a></li>          <li><a href="http://tech.firefox.sina.com/" title="科技">科技</a></li>          <li><a href="http://sports.firefox.sina.com/" title="體育">體育</a></li>          <li><a href="http://finance.firefox.sina.com/" title="財經">財經</a></li>          <li><a href="http://auto.firefox.sina.com/" title="汽車">汽車</a></li>      </ul>      <i class="iconfont icon-liebiao" id="menu"></i>  </div>"""    # 問題1:獲取所有 a 節點的文本內容  parse_html = etree.HTML(html)  r_list = parse_html.xpath('//a/text()')  print(r_list)  # ['新浪社會', '國內', '國際',.....]    # 問題2:獲取所有 a 節點的 href 的屬性值  parse_html = etree.HTML(html)  r_list = parse_html.xpath('//a/@href')  print(r_list)  # ['/', 'http://domestic.firefox.sina.com/', 'http://world.firefox.sina.com/'...]    # 問題3: 獲取所有 a 節點的href的屬性值, 但是不包括 /  parse_html = etree.HTML(html)  r_list = parse_html.xpath('//ul[@id="nav"]/li/a/@href')  print(r_list)  # ['http://domestic.firefox.sina.com/', 'http://world.firefox.sina.com/'...]    # 問題4: 獲取 圖片、軍事、...,不包括新浪社會  parse_html = etree.HTML(html)  r_list = parse_html.xpath('//ul[@id="nav"]/li/a/text()')  print(r_list)  # ['國內', '國際',.....]

貓眼電影(xpath)

地址: 貓眼電影 – 榜單 – top100榜 https://maoyan.com/board/4

目標: 電影名稱、主演、上映時間

步驟

  1. 確定是否為靜態頁面(右鍵-查看網頁源代碼,搜索關鍵字確認)
  2. 寫xpath表達式
  3. 寫程序框架

xpath表達式

1、基準xpath: 匹配所有電影信息的節點對象列表

   //dl[@class=”board-wrapper”]/dd

2、遍歷對象列表,依次獲取每個電影信息
  for dd in dd_list:

    遍歷後繼續xpath一定要以: . 開頭,代表當前節點

電影名稱 :

dd.xpath('./a/@title')[0].strip()

電影主演 :

dd.xpath('.//p[@class="star"]/text()')[0].strip()

上映時間 :

dd.xpath('.//p[@class="releasetime"]/text()')[0].strip()

完整代碼:

import requests  from lxml import etree  import time  import random      class MaoyanSpider(object):      def __init__(self):          self.page = 1  # 用於記錄頁數          self.url = 'https://maoyan.com/board/4?offset={}'          self.ua_list = [              'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.              163 Safari/535.1',              'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',              'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727;               .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)']        # 獲取頁面      def get_page(self, url):          # 每次使用隨機的user-agent          try:              # 每次使用隨機的user-agent              headers = {'User-Agent': random.choice(self.ua_list)}              res = requests.get(url, headers=headers, timeout=5)              res.encoding = 'utf-8'              html = res.text              self.parse_page(html)          except Exception as e:              print('Error')              self.get_page(url)        # 解析頁面      def parse_page(self, html):          parse_html = etree.HTML(html)       # 創建解析對象          # 基準xpath節點對象列表          dd_list = parse_html.xpath('//dl[@class="board-wrapper"]/dd')          item = {}          # 依次遍歷每個節點對象,提取數據          if dd_list:              for dd in dd_list:                  # ['喜劇之王'] 因為返回的是列表,所以取第0個值,得到的是字符串                  name_list = dd.xpath('.//p/a/@title')       # 電影名稱                  item['name'] = [name_list[0].strip() if name_list else None][0]                  star_list = dd.xpath('.//p[@class="star"]/text()')  # 電影主演                  item['star'] = [star_list[0].strip() if star_list else None][0]                  time_list = dd.xpath('.//p[@class="releasetime"]/text()')   # 上映時間                  item['time'] = [time_list[0].strip() if time_list else None]                    print(item)          else:              print('No Data')        # 主函數      def main(self):          for offset in range(0, 31, 10):              url = self.url.format(str(offset))              self.get_page(url)              print('第%d頁完成' % self.page)              time.sleep(random.randint(1, 3))              self.page += 1      if __name__ == '__main__':      start = time.time()      spider = MaoyanSpider()      spider.main()      end = time.time()      print('執行時間: %.2f' % (end - start))

鏈家二手房案例(xpath)

確定是否為靜態

  打開二手房頁面 -> 查看網頁源碼 -> 搜索關鍵字,能夠搜索到就說明,是靜態頁面。

xpath表達式

1、基準xpath表達式(匹配每個房源信息節點列表)

  //ul[@class=”sellListContent”]/li[@class=”clear LOGCLICKDATA”] | //ul[@class=”sellListContent”]/li[@class=”clear LOGVIEWDATA LOGCLICKDATA”]

2、依次遍歷後每個房源信息xpath表達式

  • 名稱: .//a[@data-el=”region”]/text()
  • 戶型+面積+方位+是否精裝:info_list = ‘.//div[@class=”houseInfo”]/text()’ [0].strip().split(‘|’)
  • 戶型: info_list[1]
  • 面積: info_list[2]
  • 方位: info_list[3]
  • 精裝: info_list[4]
  • 樓層: ‘.//div[@class=”positionInfo”]/text()’
  • 區域: ‘.//div[@class=”positionInfo”]/a/text()’
  • 總價: ‘.//div[@class=”totalPrice”]/span/text()’
  • 單價: ‘.//div[@class=”unitPrice”]/span/text()’

代碼實現

import requests  from lxml import etree  import time  import random      class LianjiaSpider(object):      def __init__(self):          self.url = 'https://bj.lianjia.com/ershoufang/pg{}/'          self.blog = 1          self.ua_list = [              'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1',              'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',              'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET               CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)', ]        def get_html(self, url):          headers = {'User-Agent': random.choice(self.ua_list)}          # 嘗試3次,否則換下一頁地址          if self.blog <= 3:              try:                  # 設定超時時間,超時後拋出異常,被except捕捉,繼續執行此函數再次請求                  res = requests.get(url=url, headers=headers, timeout=5)                  res.encoding = 'utf-8'                  html = res.text                  self.parse_page(html)       # 直接調用解析函數              except Exception as e:                  print('再次嘗試')                  self.blog += 1                  self.get_html(url)        def parse_page(self, html):          parse_html = etree.HTML(html)          # li_list: [<element li at xxx>,<element li at xxx>]          li_list = parse_html.xpath('//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]')          item = {}          for li in li_list:              name_list = li.xpath('.//a[@data-el="region"]/text()')  # 名稱              item['name'] = [name_list[0].strip() if name_list else None][0]              info_list = li.xpath('.//div[@class="houseInfo"]/text()')   # 戶型+面積+方位+是否精裝              if info_list:                  info_list = info_list[0].strip().split('|')                  if len(info_list) == 5:                      item['model'] = info_list[1].strip()                      item['area'] = info_list[2].strip()                      item['direction'] = info_list[3].strip()                      item['perfect'] = info_list[4].strip()                  else:                      item['model'] = item['area'] = item['direction'] = item['perfect'] = None              else:                  item['model'] = item['area'] = item['direction'] = item['perfect'] = None                floor_list = li.xpath('.//div[@class="positionInfo"]/text()')   # 樓層              item['floor'] = [floor_list[0].strip().split()[0] if floor_list else None][0]              address_list = li.xpath('.//div[@class="positionInfo"]/a/text()')   # 地區              item['address'] = [address_list[0].strip() if address_list else None][0]              total_list = li.xpath('.//div[@class="totalPrice"]/span/text()')    # 總價              item['total_price'] = [total_list[0].strip() if total_list else None][0]              unit_list = li.xpath('.//div[@class="unitPrice"]/span/text()')      # 單價              item['unit_price'] = [unit_list[0].strip() if unit_list else None][0]                print(item)        def main(self):          for pg in range(1, 11):              url = self.url.format(pg)              self.get_html(url)              time.sleep(random.randint(1, 3))              # 對self.blog進行一下初始化              self.blog = 1      if __name__ == '__main__':      start = time.time()      spider = LianjiaSpider()      spider.main()      end = time.time()      print('執行時間:%.2f' % (end - start))

Chrome瀏覽器安裝插件

安裝方法

  1. 把下載的相關插件(對應操作系統瀏覽器)後綴改為 .zip
  2. 打開Chrome瀏覽器 -> 右上角設置 -> 更多工具 -> 擴展程序 -> 點開開發者模式
  3. 把相關插件 拖拽 到瀏覽器中,釋放鼠標即可安裝
  4. 重啟瀏覽器

需要安裝插件

  1. Xpath Helper: 輕鬆獲取HTML元素的xPath路徑;打開/關閉: Ctrl+Shift+x
  2. Proxy SwitchyOmega: Chrome瀏覽器中的代理管理擴展程序
  3. JsonView: 格式化輸出json格式數據