天天看点

Python网络爬虫(Xpath解析, lxml库, selenium)

安装:

  Windows :安装selenium

      python -m pip install selenium

  Anaconda Prompt下执行 : 

      conda install selenium

Linux/Mac OS:

      sudo pip3 install selenium

  Ubuntu :安装Scrapy框架

    #### 依赖库较多,以下为全部依赖库,有些已安装 ####

sudo apt-get install libssl-dev
       sudo apt-get install libffi-dev 
       sudo apt-get install python3-dev
       sudo apt-get install build-essential
       sudo apt-get install libxml2
       sudo apt-get install libxml2-dev
       sudo apt-get install libxslt1-dev
       sudo apt-get install zlib1g-dev
    sudo pip3 install Scrapy           

xpath工具(解析)

  xpath

    在XML文档中查找信息的语言,同样适用于HTML文档的检索

  xpath辅助工具

    Chrome插件 :XPath Helper

      打开 :Ctrl + Shift + X

      关闭 :Ctrl + Shift + X

    Firefox插件 :XPath checker

    XPath表达式编辑工具 :XML quire

  xpath匹配规则

    匹配演示

      查找bookstore下所有节点:/bookstore

      查找所有的book节点://book

      查找所有book下的title节点中,lang属性为"en"的节点

//book/title[@lang="en"]           

      查找bookstore下的第2个book节点下的title节点:

/bookstore/book[2]/title/text()           

    选取节点

      /  : 从根节点开始选取 

      // : 从整个文档中查找节点

//price  、  /bookstore/book//price           

      @  : 选取某个节点的属性

//title[@lang="en"]           

     @的使用

      选取1个节点 : //title[@lang="en"]

      选取N个节点 : //title[@lang]

      选取节点的属性值 : //title/@lang

<a class=....,src="http://..."

    匹配多路径

      符号 : |

      获取所有book节点下的 title节点和price节点

        //book/title | //book/price

    函数

      contains() : 匹配一个属性值中包含某些字符串的节点

      //title[contains(@lang,"e")]

      text() 

        //title[contains(@lang,"e")]/text()

lxml库及xpath使用

  lxml库 :HTML/XML解析库

    安装 

python -m pip install lxml
      conda install lxml           

    使用流程

      导模块

        from lxml import etree

      利用lxml库的etree模块创建解析对象

        parseHtml = etree.HTML(html)

      解析对象调用xpath工具定位节点信息

        r_list = parseHtml.xpath('xpath表达式')

### 只要调用了xpath,结果一定是列表 ###

# 构造解析对象
parseHtml = etree.HTML(html)
# 利用解析对象调用xpath匹配
r1 = parseHtml.xpath('//a/@href')
print(r1)

# 获取 /
r2 = parseHtml.xpath('//a[@id="channel"]/@href')
print(r2)

# 获取非 /
r3 = parseHtml.xpath('//ul[@id="nav"]//a/@href')
print(r3)
# 获取所有 a 节点的文本内容
r4 = parseHtml.xpath('//a/text()')
print(r4)
# 获取 图片、军事 ... 
r5 = parseHtml.xpath('//ul[@id="nav"]//a')
for i in r5:
    print(i.text)           

    如何获取节点对象的内容

      节点对象.text

抓取百度贴吧帖子里面所有的图片

      目标 :抓取指定贴吧所有图片

      思路

        获取贴吧主页URL,下一页:找URL规律

获取1页中每个帖子的URL

对每个帖子URL发请求,获取帖子里图片URL

对图片URL发请求,以wb方式写入本地文件

      步骤

        获取贴吧主页URL

  http://tieba.baidu.com/f? + 查询参数

找到页面中所有帖子的URL

Python网络爬虫(Xpath解析, lxml库, selenium)

  src : 完整链接

  href : 和主URL进行拼接

    /p/5926064184

            http://tieba.baidu.com/p/5926064184

  xpath匹配链接:

   写法1: //div[@class="col2_right j_threadlist_li_right"]/div/div/a/@href

           写法2(推荐): //div[@class="t_con cleafix"]/div/div/div/a/@href

找每个帖子中图片URL

  Xpath匹配:

    //img[@class="BDE_Image"]/@src

'''02_百度贴吧图片抓取案例.py'''
import requests
from lxml import etree
import time

class BaiduImageSpider:
    def __init__(self):
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.baseurl = "http://tieba.baidu.com"
        self.pageurl = "http://tieba.baidu.com/f?"
        
    # 获取所有帖子URL列表
    def getPageUrl(self,params):
        res = requests.get(self.pageurl,params=params,headers=self.headers) 
        res.encoding = "utf-8"
        html = res.text
        # 构建解析对象
        parseHtml = etree.HTML(html)
        # 帖子链接列表
        t_list = parseHtml.xpath('//div[@class="t_con cleafix"]/div/div/div/a/@href')
        # t_list : ['/p/233432','/p/2039820',..]
        print(t_list)
        for t_link in t_list:
            # 拼接帖子完整链接
            t_link = self.baseurl + t_link
            self.getImageUrl(t_link)
    
    # 获取帖子中图片URL列表
    def getImageUrl(self,t_link):
        res = requests.get(t_link,headers=self.headers)
        res.encoding = "utf-8"
        html = res.text
        # 构造解析对象
        parseHtml = etree.HTML(html)
        img_list = parseHtml.xpath('//img[@class="BDE_Image"]/@src')
        print(img_list)
        for img_link in img_list:
            self.writeImage(img_link)
    
    # 保存到本地
    def writeImage(self,img_link):
        # 获取图片的bytes
        res = requests.get(img_link,headers=self.headers)
        res.encoding = "utf-8"
        html = res.content
        # filename
        filename = img_link[-12:]
        with open(filename,"wb") as f:
            f.write(html)
            time.sleep(0.5)
            print("%s下载成功" % filename)
    
    # 主函数
    def workOn(self):
        name = input("请输入贴吧名:")
        begin = int(input("请输入起始页:"))
        end = int(input("请输入终止页:"))
        
        for n in range(begin,end+1):
            pn = (n-1)*50
            params = {
                    "kw":name,
                    "pn":str(pn)
                }
            self.getPageUrl(params)
            
if __name__ == "__main__":
    spider = BaiduImageSpider()
    spider.workOn()
           

糗事百科-xpath

      目标 :用户昵称、段子内容、好笑数、评论数

        找URL

  https://www.qiushibaike.com/8hr/page/1/

xpath匹配

  基准xpath://div[contains(@id,"qiushi_tag_")]

    用户昵称: ./div/a/h2

    段子内容: .//div[@class="content"]/span

    好笑数量: .//i

    评论数量: .//i

动态网站数据抓取

  Ajax动态加载

    特点 :滚动鼠标混轮时加载

    抓包工具 :查询参数在 WebForms -> QueryString

'''03_糗事百科案例.py'''
import requests
from lxml import etree
import pymongo

class QiuShiSpider:
    def __init__(self):
        self.url = "https://www.qiushibaike.com/8hr/page/1/"
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.conn = pymongo.MongoClient("localhost",27017)
        self.db = self.conn.Baikedb
        self.myset = self.db.baikeset
        
    def getPage(self):
        res = requests.get(self.url,headers=self.headers)
        res.encoding = "utf-8"
        html = res.text
        self.parsePage(html)
    
    def parsePage(self,html):
        parseHtml = etree.HTML(html)
        # 基准xpath,每个段子的列表
        base_list = parseHtml.xpath('//div[contains(@id,"qiushi_tag_")]')
        # 遍历每个段子的节点对象(base)
        for base in base_list:
            # 用户昵称
            username = base.xpath('./div/a/h2')
            if len(username) == 0:
                username = "匿名用户"
            else:
                username = username[0].text
            # 段子内容
            content = base.xpath('.//div[@class="content"]/span')[0].text
            # 好笑数量
            # [<element.好笑数量>,<eleme.评论>,<element...>]
            laughNum = base.xpath('.//i')[0].text 
            # 评论数量
            pingNum = base.xpath('.//i')[1].text
            
            d = {
                  "username":username.strip(),
                  "content":content.strip(),
                  "laughNum":laughNum.strip(),
                  "pingNum":pingNum.strip()
              }
            self.myset.insert(d)
            print("存入数据库成功")


if __name__ == "__main__":
    spider = QiuShiSpider()
    spider.getPage()