Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector

From:http://cuiqingcai.com/2621.html

The lxml.etree Tutorial :https://lxml.de/tutorial.htmlpython3 解析 xml:https://www.cnblogs.com/deadwood-2016/p/8116863.html
微软文档:  XPath 语法 和 XPath 函数
W3school Xpath 教程:http://www.w3school.com.cn/xpath/
Xpath 菜鸟教程:http://www.runoob.com/xpath/xpath-tutorial.html
简书:Xpath高级用法:https://www.jianshu.com/p/1575db75670f
30个示例手把手教你学会Xpath高级用法:https://www.sohu.com/a/211716225_236714
了解XPath常用术语和表达式解析 十分钟轻松入门:http://www.bazhuayu.com/blog/2014091

 

 

前言

 

XPath 即为 XML 路径语言,它是一种用来确定 XML(标准通用标记语言的子集)文档中某部分位置的语言。
XPath 基于 XML 的树状结构,提供在数据结构树中找寻节点的能力。 XPath 同样也支持HTML。
XPath 是一门小型的查询语言。
python 中 lxml库 使用的是 Xpath 语法,是效率比较高的解析方法。

lxml 用法源自 lxml python 官方文档:http://lxml.de/index.html
XPath 语法参考 w3school:http://www.w3school.com.cn/xpath/index.asp

 

 

安装

pip3 install lxml

 

 

Python 中如何安装使用 XPath

 

step1: 安装 lxml 库
step2: from lxml import etree 
etree全称:ElementTree 元素树
step3: selector = etree.HTML(网页源代码)
step4: selector.xpath(一段神奇的符号)

 

lxml 使用 Xpath 使用示例:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author      : 
# @File        : test.py
# @Software    : PyCharm
# @description : XXX


from lxml import etree


html = '''
  • 有效信息1
  • 有效信息2
  • 有效信息3
  • 无效信息1
  • 无效信息2
  • 无效信息3
''' def test(): selector = etree.HTML(html) # 提取 li 中的有效信息123 content = selector.xpath('//ul[@id="useful"]/li/text()') for each in content: print(each) # 提取 a 中的属性 link = selector.xpath('//a/@href') for each in link: print(each) title = selector.xpath('//a/@title') for each in title: print(each) if __name__ == '__main__': test()

lxml 使用 CSS 选择器 使用 示例 1:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author      : 
# @File        : test_2.py
# @Software    : PyCharm
# @description : XXX


import lxml.html
from urllib.request import urlopen


def scrape(html):
    tree = lxml.html.fromstring(html)
    td = tree.cssselect('tr#places_neighbours__row > td.w2p_fw')[0]
    area = td.text_content()
    return area


if __name__ == '__main__':
    r_html = urlopen('http://example.webscraping.com/view/United-Kingdom-239').read()
    print(scrape(r_html))

lxml 使用 CSS 选择器 使用 示例2 :

# -*- coding: utf-8 -*-

import csv
import re
import urlparse
import lxml.html
from link_crawler import link_crawler

FIELDS = ('area', 'population', 'iso', 'country', 'capital', 'continent', 
          'tld', 'currency_code', 'currency_name', 'phone', 'postal_code_format', 
          'postal_code_regex', 'languages', 'neighbours')


def scrape_callback(url, html):
    if re.search('/view/', url):
        tree = lxml.html.fromstring(html)
        row = [tree.cssselect('table > tr#places_{}__row > td.w2p_fw'.format(field))[0].text_content() for field in FIELDS]
        print url, row


if __name__ == '__main__':
    link_crawler('http://example.webscraping.com/', '/(index|view)', scrape_callback=scrape_callback)

link_crawler.py

import re
import urlparse
import urllib2
import time
from datetime import datetime
import robotparser
import Queue


def link_crawler(seed_url, link_regex=None, delay=5, max_depth=-1, max_urls=-1, headers=None, user_agent='wswp', proxy=None, num_retries=1, scrape_callback=None):
    """Crawl from the given seed URL following links matched by link_regex
    """
    # the queue of URL's that still need to be crawled
    crawl_queue = [seed_url]
    # the URL's that have been seen and at what depth
    seen = {seed_url: 0}
    # track how many URL's have been downloaded
    num_urls = 0
    rp = get_robots(seed_url)
    throttle = Throttle(delay)
    headers = headers or {}
    if user_agent:
        headers['User-agent'] = user_agent

    while crawl_queue:
        url = crawl_queue.pop()
        depth = seen[url]
        # check url passes robots.txt restrictions
        if rp.can_fetch(user_agent, url):
            throttle.wait(url)
            html = download(url, headers, proxy=proxy, num_retries=num_retries)
            links = []
            if scrape_callback:
                links.extend(scrape_callback(url, html) or [])

            if depth != max_depth:
                # can still crawl further
                if link_regex:
                    # filter for links matching our regular expression
                    links.extend(link for link in get_links(html) if re.match(link_regex, link))

                for link in links:
                    link = normalize(seed_url, link)
                    # check whether already crawled this link
                    if link not in seen:
                        seen[link] = depth + 1
                        # check link is within same domain
                        if same_domain(seed_url, link):
                            # success! add this new link to queue
                            crawl_queue.append(link)

            # check whether have reached downloaded maximum
            num_urls += 1
            if num_urls == max_urls:
                break
        else:
            print 'Blocked by robots.txt:', url


class Throttle:
    """Throttle downloading by sleeping between requests to same domain
    """
    def __init__(self, delay):
        # amount of delay between downloads for each domain
        self.delay = delay
        # timestamp of when a domain was last accessed
        self.domains = {}
        
    def wait(self, url):
        """Delay if have accessed this domain recently
        """
        domain = urlparse.urlsplit(url).netloc
        last_accessed = self.domains.get(domain)
        if self.delay > 0 and last_accessed is not None:
            sleep_secs = self.delay - (datetime.now() - last_accessed).seconds
            if sleep_secs > 0:
                time.sleep(sleep_secs)
        self.domains[domain] = datetime.now()



def download(url, headers, proxy, num_retries, data=None):
    print 'Downloading:', url
    request = urllib2.Request(url, data, headers)
    opener = urllib2.build_opener()
    if proxy:
        proxy_params = {urlparse.urlparse(url).scheme: proxy}
        opener.add_handler(urllib2.ProxyHandler(proxy_params))
    try:
        response = opener.open(request)
        html = response.read()
        code = response.code
    except urllib2.URLError as e:
        print 'Download error:', e.reason
        html = ''
        if hasattr(e, 'code'):
            code = e.code
            if num_retries > 0 and 500 <= code < 600:
                # retry 5XX HTTP errors
                html = download(url, headers, proxy, num_retries-1, data)
        else:
            code = None
    return html


def normalize(seed_url, link):
    """Normalize this URL by removing hash and adding domain
    """
    link, _ = urlparse.urldefrag(link) # remove hash to avoid duplicates
    return urlparse.urljoin(seed_url, link)


def same_domain(url1, url2):
    """Return True if both URL's belong to same domain
    """
    return urlparse.urlparse(url1).netloc == urlparse.urlparse(url2).netloc


def get_robots(url):
    """Initialize robots parser for this domain
    """
    rp = robotparser.RobotFileParser()
    rp.set_url(urlparse.urljoin(url, '/robots.txt'))
    rp.read()
    return rp
        

def get_links(html):
    """Return a list of links from html 
    """
    # a regular expression to extract all links from the webpage
    webpage_regex = re.compile(']+href=["\'](.*?)["\']', re.IGNORECASE)
    # list of all links from the webpage
    return webpage_regex.findall(html)


if __name__ == '__main__':
    link_crawler('http://example.webscraping.com', '/(index|view)', delay=0, num_retries=1, user_agent='BadCrawler')
    link_crawler('http://example.webscraping.com', '/(index|view)', delay=0, num_retries=1, max_depth=1, user_agent='GoodCrawler')

 

 

 

Python3 解析 XML

 

来源:https://www.cnblogs.com/deadwood-2016/p/8116863.html

Python 使用 XPath 解析 XML文档 :http://www.jingfengshuo.com/archives/1414.html

 

在 XML 解析方面,Python 贯彻了自己“开箱即用”(batteries included)的原则。在自带的标准库中,Python提供了大量可以用于处理XML语言的包和工具,数量之多,甚至让Python编程新手无从选择。

本文将介绍深入解读利用Python语言解析XML文件的几种方式,并以笔者推荐使用的ElementTree模块为例,演示具体使用方法和场景。

 

 

一、什么是 XML?

XML是可扩展标记语言(Extensible Markup Language)的缩写,其中的 标记(markup)是关键部分。您可以创建内容,然后使用限定标记标记它,从而使每个单词、短语或块成为可识别、可分类的信息。

Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第1张图片

标记语言从早期的私有公司和政府制定形式逐渐演变成标准通用标记语言(Standard Generalized Markup Language,SGML)、超文本标记语言(Hypertext Markup Language,HTML),并且最终演变成 XML。XML有以下几个特点。

  • XML的设计宗旨是传输数据,而非显示数据。
  • XML标签没有被预定义。您需要自行定义标签。
  • XML被设计为具有自我描述性。
  • XML是W3C的推荐标准。

目前,XML在Web中起到的作用不会亚于一直作为Web基石的HTML。 XML无所不在。XML是各种应用程序之间进行数据传输的最常用的工具,并且在信息存储和描述领域变得越来越流行。因此,学会如何解析XML文件,对于Web开发来说是十分重要的。

Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第2张图片

 

二、有哪些可以解析 XML 的 Python 包 ?

Python的标准库中,提供了6种可以用于处理XML的包。

xml.dom

xml.dom实现的是W3C制定的DOM API。如果你习惯于使用DOM API或者有人要求这这样做,可以使用这个包。不过要注意,在这个包中,还提供了几个不同的模块,各自的性能有所区别。

Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第3张图片

DOM解析器在任何处理开始之前,必须把基于XML文件生成的树状数据放在内存,所以DOM解析器的内存使用量完全根据输入资料的大小。

xml.dom.minidom

xml.dom.minidom是DOM API的极简化实现,比完整版的DOM要简单的多,而且这个包也小的多。那些不熟悉DOM的朋友,应该考虑使用xml.etree.ElementTree模块。据lxml的作者评价,这个模块使用起来并不方便,效率也不高,而且还容易出现问题。

xml.dom.pulldom

与其他模块不同,xml.dom.pulldom模块提供的是一个“pull解析器”,其背后的基本概念指的是从XML流中pull事件,然后进行处理。虽然与SAX一样采用事件驱动模型(event-driven processing model),但是不同的是,使用pull解析器时,使用者需要明确地从XML流中pull事件,并对这些事件遍历处理,直到处理完成或者出现错误。

  • pull解析(pull parsing)是近来兴起的一种XML处理趋势。此前诸如SAX和DOM这些流行的XML解析框架,都是push-based,也就是说对解析工作的控制权,掌握在解析器的手中。

xml.sax

Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第4张图片

xml.sax模块实现的是SAX API,这个模块牺牲了便捷性来换取速度和内存占用。SAX是Simple API for XML的缩写,它并不是由W3C官方所提出的标准。它是事件驱动的,并不需要一次性读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。

xml.parser.expat

xml.parser.expat提供了对C语言编写的expat解析器的一个直接的、底层API接口。expat接口与SAX类似,也是基于事件回调机制,但是这个接口并不是标准化的,只适用于expat库。

expat是一个面向流的解析器。您注册的解析器回调(或handler)功能,然后开始搜索它的文档。当解析器识别该文件的指定的位置,它会调用该部分相应的处理程序(如果您已经注册的一个)。该文件被输送到解析器,会被分割成多个片断,并分段装到内存中。因此expat可以解析那些巨大的文件。

xml.etree.ElementTree(以下简称ET)

Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第5张图片

xml.etree.ElementTree模块提供了一个轻量级、Pythonic的API,同时还有一个高效的C语言实现,即xml.etree.cElementTree。与DOM相比,ET的速度更快,API使用更直接、方便。与SAX相比,ET.iterparse函数同样提供了按需解析的功能,不会一次性在内存中读入整个文档。ET的性能与SAX模块大致相仿,但是它的API更加高层次,用户使用起来更加便捷。

笔者建议,在使用Python进行XML解析时,首选使用ET模块,除非你有其他特别的需求,可能需要另外的模块来满足。

  • 解析XML的这几种API并不是Python独创的,Python也是通过借鉴其他语言或者直接从其他语言引入进来的。例如expat就是一个用C语言开发的、用来解析XML文档的开发库。而SAX最初是由DavidMegginson采用java语言开发的,DOM可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构,可以应用于任何编程语言。

下面,我们以ElementTree模块为例,介绍在Python中如何解析lxml。

 

三、利用 ElementTree 解析 XML

Python标准库中,提供了ET的两种实现。一个是纯Python实现的xml.etree.ElementTree,另一个是速度更快的C语言实现xml.etree.cElementTree。请记住始终使用C语言实现,因为它的速度要快很多,而且内存消耗也要少很多。如果你所使用的Python版本中没有cElementTree所需的加速模块,你可以这样导入模块:

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

如果某个API存在不同的实现,上面是常见的导入方式。当然,很可能你直接导入第一个模块时,并不会出现问题。请注意,自Python 3.3之后,就不用采用上面的导入方法,因为ElemenTree模块会自动优先使用C加速器,如果不存在C实现,则会使用Python实现。因此,使用Python 3.3+的朋友,只需要import xml.etree.ElementTree即可。

1、将XML文档解析为树(tree)

我们先从基础讲起。XML是一种结构化、层级化的数据格式,最适合体现XML的数据结构就是树。ET提供了两个对象:ElementTree将整个XML文档转化为树,Element则代表着树上的单个节点。对整个XML文档的交互(读取,写入,查找需要的元素),一般是在ElementTree层面进行的。对单个XML元素及其子元素,则是在Element层面进行的。下面我们举例介绍主要使用方法。

我们使用下面的XML文档,作为演示数据:



  
    text,source
  
  
    
      xml,sgml
    
  
  
  

接下来,我们加载这个文档,并进行解析:

>>> import xml.etree.ElementTree as ET
>>> tree = ET.ElementTree(file='doc1.xml')

然后,我们获取根元素(root element):

>>> tree.getroot()

正如之前所讲的,根元素(root)是一个Element对象。我们看看根元素都有哪些属性:

>>> root = tree.getroot()
>>> root.tag, root.attrib
('doc', {})

没错,根元素并没有属性。与其他Element对象一样,根元素也具备遍历其直接子元素的接口:

>>> for child_of_root in root:
...  print child_of_root.tag, child_of_root.attrib
...
branch {'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}

我们还可以通过索引值来访问特定的子元素:

>>> root[0].tag, root[0].text
('branch', '\n    text,source\n  ')

2、查找需要的元素

从上面的示例中,可以明显发现我们能够通过简单的递归方法(对每一个元素,递归式访问其所有子元素)获取树中的所有元素。但是,由于这是十分常见的工作,ET提供了一些简便的实现方法。

Element对象有一个iter方法,可以对某个元素对象之下所有的子元素进行深度优先遍历(DFS)。ElementTree对象同样也有这个方法。下面是查找XML文档中所有元素的最简单方法:

>>> for elem in tree.iter():
...  print elem.tag, elem.attrib
...
doc {}
branch {'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
sub-branch {'name': 'subrelease01'}
branch {'name': 'invalid'}

在此基础上,我们可以对树进行任意遍历——遍历所有元素,查找出自己感兴趣的属性。但是ET可以让这个工作更加简便、快捷。iter方法可以接受tag名称,然后遍历所有具备所提供tag的元素:

>>> for elem in tree.iter(tag='branch'):
...  print elem.tag, elem.attrib
...
branch {'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}

3、支持通过 XPath 查找元素

使用XPath查找感兴趣的元素,更加方便。Element对象中有一些find方法可以接受Xpath路径作为参数,find方法会返回第一个匹配的子元素,findall以列表的形式返回所有匹配的子元素, iterfind则返回一个所有匹配元素的迭代器(iterator)。ElementTree对象也具备这些方法,相应地它的查找是从根节点开始的。

下面是一个使用XPath查找元素的示例:

>>> for elem in tree.iterfind('branch/sub-branch'):
...  print elem.tag, elem.attrib
...
sub-branch {'name': 'subrelease01'}

上面的代码返回了branch元素之下所有tag为sub-branch的元素。接下来查找所有具备某个name属性的branch元素:

>>> for elem in tree.iterfind('branch[@name="release01"]'):
...  print elem.tag, elem.attrib
...
branch {'hash': 'f200013e', 'name': 'release01'}

4、构建 XML 文档

利用ET,很容易就可以完成XML文档构建,并写入保存为文件。ElementTree对象的write方法就可以实现这个需求。

一般来说,有两种主要使用场景。一是你先读取一个XML文档,进行修改,然后再将修改写入文档,二是从头创建一个新XML文档。

修改文档的话,可以通过调整Element对象来实现。请看下面的例子:

>>> root = tree.getroot()
>>> del root[2]
>>> root[0].set('foo', 'bar')
>>> for subelem in root:
...  print subelem.tag, subelem.attrib
...
branch {'foo': 'bar', 'hash': '1cdf045c', 'name': 'codingpy.com'}
branch {'hash': 'f200013e', 'name': 'release01'}

在上面的代码中,我们删除了root元素的第三个子元素,为第一个子元素增加了新属性。这个树可以重新写入至文件中。最终的XML文档应该是下面这样的:

>>> import sys
>>> tree.write(sys.stdout)

  
    text,source
  
  
    
      xml,sgml
    
  
  

请注意,文档中元素的属性顺序与原文档不同。这是因为ET是以字典的形式保存属性的,而字典是一个无序的数据结构。当然,XML也不关注属性的顺序。

从头构建一个完整的文档也很容易。ET模块提供了一个SubElement工厂函数,让创建元素的过程变得很简单:

>>> a = ET.Element('elem')
>>> c = ET.SubElement(a, 'child1')
>>> c.text = "some text"
>>> d = ET.SubElement(a, 'child2')
>>> b = ET.Element('elem_b')
>>> root = ET.Element('root')
>>> root.extend((a, b))
>>> tree = ET.ElementTree(root)
>>> tree.write(sys.stdout)
some text

5、利用iterparse解析XML流

XML文档通常都会比较大,如何直接将文档读入内存的话,那么进行解析时就会出现问题。这也就是为什么不建议使用DOM,而是SAX API的理由之一。

我们上面谈到,ET可以将XML文档加载为保存在内存里的树(in-memory tree),然后再进行处理。但是在解析大文件时,这应该也会出现和DOM一样的内存消耗大的问题吧?没错,的确有这个问题。为了解决这个问题,ET提供了一个类似SAX的特殊工具——iterparse,可以循序地解析XML。

接下来,笔者为大家展示如何使用iterparse,并与标准的树解析方式进行对比。我们使用一个自动生成的XML文档,下面是该文档的开头部分:



 
  
   
    United States  
    1
    duteous nine eighteen 
    Creditcard
    
     
[...]

我们来统计一下文档中出现了多少个文本值为Zimbabwe的location元素。下面是使用ET.parse的标准方法:

tree = ET.parse(sys.argv[2])
 
count = 0
for elem in tree.iter(tag='location'):
  if elem.text == 'Zimbabwe':
    count += 1
 
print count

上面的代码会将全部元素载入内存,逐一解析。当解析一个约100MB的XML文档时,运行上面脚本的Python进程的内存使用峰值为约560MB,总运行时间问2.9秒。

请注意,我们其实不需要讲整个树加载到内存里。只要检测出文本为相应值得location元素即可。其他数据都可以废弃。这时,我们就可以用上iterparse方法了:

count = 0
for event, elem in ET.iterparse(sys.argv[2]):
  if event == 'end':
    if elem.tag == 'location' and elem.text == 'Zimbabwe':
      count += 1
  elem.clear() # 将元素废弃
 
print count

上面的for循环会遍历iterparse事件,首先检查事件是否为end,然后判断元素的tag是否为location,以及其文本值是否符合目标值。另外,调用elem.clear()非常关键:因为iterparse仍然会生成一个树,只是循序生成的而已。废弃掉不需要的元素,就相当于废弃了整个树,释放出系统分配的内存。

当利用上面这个脚本解析同一个文件时,内存使用峰值只有7MB,运行时间为2.5秒。速度提升的原因,是我们这里只在树被构建时,遍历一次。而使用parse的标准方法是先完成整个树的构建后,才再次遍历查找所需要的元素。

iterparse的性能与SAX相当,但是其API却更加有用:iterparse会循序地构建树;而利用SAX时,你还得自己完成树的构建工作。

 

四、使用示例:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author      : 
# @File        : test_1.py
# @Software    : PyCharm
# @description : XXX


"""
Element是 XML处理的核心类,
Element对象可以直观的理解为 XML的节点,大部分 XML节点的处理都是围绕该类进行的。
这部分包括三个内容:节点的操作、节点属性的操作、节点内文本的操作。
"""

from lxml import etree
import lxml.html as HTML

# 1.创建element
root = etree.Element('root')
print(root, root.tag)

# 2.添加子节点
child1 = etree.SubElement(root, 'child1')
child2 = etree.SubElement(root, 'child2')

# 3.删除子节点
# root.remove(child2)

# 4.删除所有子节点
# root.clear()

# 5.以列表的方式操作子节点
print(len(root))
print(root.index(child1))  # 索引号
root.insert(0, etree.Element('child3'))  # 按位置插入
root.append(etree.Element('child4'))  # 尾部添加

# 6.获取父节点
print(child1.getparent().tag)
# print root[0].getparent().tag   #用列表获取子节点,再获取父节点
'''以上都是节点操作'''

# 7.创建属性
# root.set('hello', 'dahu')   #set(属性名,属性值)
# root.set('hi', 'qing')

# 8.获取属性
# print(root.get('hello'))    #get方法
# print root.keys(),root.values(),root.items()    #参考字典的操作
# print root.attrib           #直接拿到属性存放的字典,节点的attrib,就是该节点的属性
'''以上是属性的操作'''

# 9.text和tail属性
# root.text = 'Hello, World!'
# print root.text

# 10.test,tail 和 text 的结合
html = etree.Element('html')
html.text = 'html.text'
body = etree.SubElement(html, 'body')
body.text = 'wo ai ni'
child = etree.SubElement(body, 'child')
child.text = 'child.text'  # 一般情况下,如果一个节点的text没有内容,就只有符号,如果有内容,才会<>,都有
child.tail = 'tails'       # tail是在标签后面追加文本
print(etree.tostring(html))
# print(etree.tostring(html, method='text'))  # 只输出text和tail这种文本文档,输出的内容连在一起,不实用

# 11.Xpath方式
# print(html.xpath('string()'))   #这个和上面的方法一样,只返回文本的text和tail
print(html.xpath('//text()'))  # 这个比较好,按各个文本值存放在列表里面
tt = html.xpath('//text()')
print(tt[0].getparent().tag)  # 这个可以,首先我可以找到存放每个节点的text的列表,然后我再根据text找相应的节点
# for i in tt:
#     print i,i.getparent().tag,'\t',

# 12.判断文本类型
print(tt[0].is_text, tt[-1].is_tail)  # 判断是普通text文本,还是tail文本
'''以上都是文本的操作'''

# 13.字符串解析,fromstring方式
xml_data = 'html.textwo ai nichild.texttails'
root1 = etree.fromstring(xml_data)  # fromstring,字面意思,直接来源字符串
# print root1.tag
# print etree.tostring(root1)

# 14.xml方式
root2 = etree.XML(xml_data)  # 和fromstring基本一样,
print(etree.tostring(root2))

# 15.文件类型解析
'''
- a file name/path
- a file object
- a file-like object
- a URL using the HTTP or FTP protocol
'''
tree = etree.parse('text.html')  # 文件解析成元素树
root3 = tree.getroot()  # 获取元素树的根节点
print(etree.tostring(root3, pretty_print=True))

parser = etree.XMLParser(remove_blank_text=True)  # 去除xml文件里的空行
root = etree.XML("            ", parser)
print(etree.tostring(root))

# 16.html方式
xml_data1 = 'data'
root4 = etree.HTML(xml_data1)
print(etree.tostring(root4))  # HTML方法,如果没有和标签,会自动补上
# 注意,如果是需要补全的html格式:这样处理哦
with open("quotes-1.html", 'r') as f:
    a = HTML.document_fromstring(f.read().decode("utf-8"))

for i in a.xpath('//div[@class="quote"]/span[@class="text"]/text()'):
    print(i)

# 17.输出内容,输出xml格式
print(etree.tostring(root))
print(etree.tostring(root, xml_declaration=True, pretty_print=True, encoding='utf-8'))  # 指定xml声明和编码
'''以上是文件IO操作'''

# 18.findall方法
root = etree.XML("aText")
print(root.findall('a')[0].text)  # findall操作返回列表
print(root.find('.//a').text)  # find操作就相当与找到了这个元素节点,返回匹配到的第一个元素
print(root.find('a').text)
print([b.text for b in root.findall('.//a')])  # 配合列表解析,相当帅气!
print(root.findall('.//a[@x]')[0].tag)  # 根据属性查询
'''以上是搜索和定位操作'''
print(etree.iselement(root))
print(root[0] is root[1].getprevious())  # 子节点之间的顺序
print(root[1] is root[0].getnext())
'''其他技能'''
# 遍历元素数
root = etree.Element("root")
etree.SubElement(root, "child").text = "Child 1"
etree.SubElement(root, "child").text = "Child 2"
etree.SubElement(root, "another").text = "Child 3"
etree.SubElement(root[0], "childson").text = "son 1"
# for i in root.iter():   #深度遍历
# for i in root.iter('child'):    #只迭代目标值
#     print i.tag,i.text
# print etree.tostring(root,pretty_print=True)

简单的创建和遍历

from lxml import etree


# 创建
root = etree.Element('root')
# 添加子元素,并为子节点添加属性
root.append(etree.Element('child',interesting='sss'))
# 另一种添加子元素的方法
body = etree.SubElement(root,'body')
body.text = 'TEXT' # 设置值
body.set('class','dd') # 设置属性
//
# 输出整个节点
print(etree.tostring(root, encoding='UTF-8', pretty_print=True))
//
//
# 创建,添加子节点、文本、注释
root = etree.Element('root')
etree.SubElement(root, 'child').text = 'Child 1'
etree.SubElement(root, 'child').text = 'Child 2'
etree.SubElement(root, 'child').text = 'Child 3'
root.append(etree.Entity('#234'))
root.append(etree.Comment('some comment'))  # 添加注释
# 为第三个节点添加一个br
br = etree.SubElement(root.getchildren()[2],'br')
br.tail = 'TAIL'
for element in root.iter(): # 也可以指定只遍历是Element的,root.iter(tag=etree.Element)
   if isinstance(element.tag, str):
        print('%s - %s' % (element.tag, element.text))
    else:
        print('SPECIAL: %s - %s' % (element, element.text))

对HTML/XML的解析

# 先导入相关模块
from lxml import etree
from io import StringIO, BytesIO
# 对html具有修复标签补全的功能
broken_html = 'test<body><h1 class="hh">page title</h3>'
parser = etree.HTMLParser()
tree = etree.parse(StringIO(broken_html), parser) # 或者直接使用 html = etree.HTML(broken_html)
print(etree.tostring(tree, pretty_print=True, method="html"))
#
#
#用xpath获取h1
h1 = tree.xpath('//h1')  # 返回的是一个数组
# 获取第一个的tag
print(h1[0].tag)
# 获取第一个的class属性
print(h1[0].get('class'))
# 获取第一个的文本内容
print(h1[0].text)
# 获取所有的属性的key,value的列表
print(h1[0].keys(),h1[0].values())</code></pre> 
  <p> </p> 
  <p>杂项</p> 
  <p>python3.5 lxml用法<br> 问题1:有一个XML文件,如何解析 <br> 问题2:解析后,如果查找、定位某个标签 <br> 问题3:定位后如何操作标签,比如访问属性、文本内容等 <br> 开始之前,首先是导入模块,该库常用的XML处理功能都在lxml.etree中<br> 导入模块:from lxml import etree </p> 
  <p><br> Element类</p> 
  <p>Element是XML处理的核心类,Element对象可以直观的理解为XML的节点,大部分XML节点的处理都是围绕该类进行的。<br> 这部分包括三个内容:节点的操作、节点属性的操作、节点内文本的操作。</p> 
  <p>节点操作</p> 
  <p>1、创建Element对象<br>         直接使用Element方法,参数即节点名称。<br>         root = etree.Element(‘root’) <br>         print(root) </p> 
  <p>2、获取节点名称<br>         使用tag属性,获取节点的名称。<br>         print(root.tag) <br>         root </p> 
  <p>3、输出XML内容<br>         使用tostring方法输出XML内容(后文还会有补充介绍),参数为Element对象。<br>         print(etree.tostring(root)) <br>         b’’ <br> 4、添加子节点<br>         使用SubElement方法创建子节点,第一个参数为父节点(Element对象),第二个参数为子节点名称。<br>         child1 = etree.SubElement(root, ‘child1’) <br>         child2 = etree.SubElement(root, ‘child2’) <br>         child3 = etree.SubElement(root, ‘child3’) <br> 5、删除子节点<br>         使用remove方法删除指定节点,参数为Element对象。clear方法清空所有节点。<br>         root.remove(child1) # 删除指定子节点 <br>         print(etree.tostring(root)) <br>         b’’ <br>         root.clear() # 清除所有子节点 <br>         print(etree.tostring(root)) <br>         b’’ <br> 6、以列表的方式操作子节点<br>         可以将Element对象的子节点视为列表进行各种操作:<br>         child = root[0] # 下标访问 <br>         print(child.tag) <br>         child1<br>         print(len(root)) # 子节点数量 <br>         3<br>         root.index(child2) # 获取索引号 <br>         1<br>         for child in root: # 遍历 <br>         … print(child.tag) <br>         child1 <br>         child2 <br>         child3<br>         root.insert(0, etree.Element(‘child0’)) # 插入 <br>         start = root[:1] # 切片 <br>         end = root[-1:]<br>         print(start[0].tag) <br>         child0 <br>         print(end[0].tag) <br>         child3<br>         root.append( etree.Element(‘child4’) ) # 尾部添加 <br>         print(etree.tostring(root)) <br>         b’’ <br>         其实前面讲到的删除子节点的两个方法remove和clear也和列表相似。</p> 
  <p>7、获取父节点<br>         使用getparent方法可以获取父节点。<br>         print(child1.getparent().tag) <br>         root </p> 
  <p><br> 属性操作</p> 
  <p>属性是以key-value的方式存储的,就像字典一样。</p> 
  <p>1、创建属性</p> 
  <p>        可以在创建Element对象时同步创建属性,第二个参数即为属性名和属性值:<br>         root = etree.Element(‘root’, interesting=’totally’) <br>         print(etree.tostring(root)) <br>         b’’ <br>         也可以使用set方法给已有的Element对象添加属性,两个参数分别为属性名和属性值:</p> 
  <p>        root.set(‘hello’, ‘Huhu’) <br>         print(etree.tostring(root)) <br>         b’’<br>         <br> 2、获取属性<br>         属性是以key-value的方式存储的,就像字典一样。直接看例子</p> 
  <p>        get方法获得某一个属性值<br>         print(root.get(‘interesting’)) <br>         totally<br>         keys方法获取所有的属性名<br>         sorted(root.keys()) <br>         [‘hello’, ‘interesting’]</p> 
  <p>        items方法获取所有的键值对<br>         for name, value in sorted(root.items()): <br>             … print(‘%s = %r’ % (name, value)) <br>         hello = ‘Huhu’ <br>         interesting = ‘totally’ <br>         <br>         也可以用attrib属性一次拿到所有的属性及属性值存于字典中:<br>         attributes = root.attrib <br>         print(attributes) <br>         {‘interesting’: ‘totally’, ‘hello’: ‘Huhu’}</p> 
  <p>        attributes[‘good’] = ‘Bye’ # 字典的修改影响节点 <br>         print(root.get(‘good’)) <br>         Bye </p> 
  <p><br> 文本操作</p> 
  <p>标签及标签的属性操作介绍完了,最后就剩下标签内的文本了。<br> 可以使用text和tail属性、或XPath的方式来访问文本内容。</p> 
  <p>1、text 和 tail 属性</p> 
  <p>        一般情况,可以用Element的text属性访问标签的文本。<br>         root = etree.Element(‘root’) <br>         root.text = ‘Hello, World!’ <br>         print(root.text) <br>         Hello, World! <br>         print(etree.tostring(root)) <br>         b’Hello, World!’ </p> 
  <p>        Element类提供了tail属性支持单一标签的文本获取。<br>         html = etree.Element(‘html’) <br>         body = etree.SubElement(html, ‘body’) <br>         body.text = ‘Text’ <br>         print(etree.tostring(html)) <br>         b’Text’<br>         <br>         br = etree.SubElement(body, ‘br’) <br>         print(etree.tostring(html)) <br>         b’Text’<br>                 <br>         tail仅在该标签后面追加文本       <br>         br.tail = ‘Tail’ <br>         print(etree.tostring(br)) <br>         b’<br>         Tail’<br>         <br>         print(etree.tostring(html)) <br>         b’Text<br>         Tail’<br>         <br>         tostring方法增加method参数,过滤单一标签,输出全部文本<br>                 <br>         print(etree.tostring(html, method=’text’)) <br>         b’TextTail’ <br>         <br> 2、XPath方式<br>         方式一:过滤单一标签,返回文本<br>         print(html.xpath(‘string()’)) <br>         TextTail<br>         <br>         方式二:返回列表,以单一标签为分隔<br>         <br>         print(html.xpath(‘//text()’)) <br>         [‘Text’, ‘Tail’] <br>         方法二获得的列表,每个元素都会带上它所属节点及文本类型信息,如下:<br>         <br>         texts = html.xpath(‘//text()’))<br>         <br>         print(texts[0]) <br>         Text</p> 
  <p><br> 所属节点<br>         parent = texts[0].getparent() <br>         print(parent.tag) <br>         body<br>         <br>         print(texts[1], texts[1].getparent().tag) <br>         Tail br</p> 
  <p>文本类型:是普通文本还是tail文本<br>         print(texts[0].is_text) <br>         True <br>         print(texts[1].is_text) <br>         False <br>         print(texts[1].is_tail) <br>         True </p> 
  <p><br> 文件 解析 与 输出<br> 回答问题1。</p> 
  <p>这部分讲述如何将XML文件解析为Element对象,以及如何将Element对象输出为XML文件。</p> 
  <p>1、文件解析</p> 
  <p>        文件解析常用的有fromstring、XML 和 HTML 三个方法。接受的参数都是字符串。<br>         xml_data = ‘data’<br>         fromstring方法<br>         root1 = etree.fromstring(xml_data) <br>         print(root1.tag) <br>         root <br>         print(etree.tostring(root1)) <br>         b’data’</p> 
  <p>        XML方法,与fromstring方法基本一样</p> 
  <p>        root2 = etree.XML(xml_data) <br>         print(root2.tag) <br>         root <br>         print(etree.tostring(root2)) <br>         b’data’</p> 
  <p><br>         HTML方法,如果没有和标签,会自动补上<br>         root3 = etree.HTML(xml_data) <br>         print(root3.tag) <br>         html <br>         print(etree.tostring(root3)) <br>         b’data’ <br>         <br> 2、输出<br>         输出其实就是前面一直在用的tostring方法了,这里补充xml_declaration和encoding两个参数,前者是XML声明,后者是指定编码。<br>         root = etree.XML(‘‘)<br>         print(etree.tostring(root)) <br>         b’’</p> 
  <p><br> XML声明<br> print(etree.tostring(root, xml_declaration=True)) <br> b”</p> 
  <p><br> 指定编码</p> 
  <p>print(etree.tostring(root, encoding=’iso-8859-1’)) <br> b”</p> 
  <p>查找第一个b标签<br> print(root.find(‘b’)) <br> None <br> print(root.find(‘a’).tag) <br> a</p> 
  <p>查找所有b标签,返回Element对象组成的列表</p> 
  <p>[ b.tag for b in root.findall(‘.//b’) ] <br> [‘b’, ‘b’]</p> 
  <p><br> 根据属性查询<br> print(root.findall(‘.//a[@x]’)[0].tag) <br> a <br> print(root.findall(‘.//a[@y]’)) <br> [] <br>  </p> 
  <p> </p> 
  <p> </p> 
  <h1>XPath语法</h1> 
  <p> </p> 
  <p>XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XPointer 都构建于 XPath 表达之上。</p> 
  <p> </p> 
  <p><strong>XPath 常用规则</strong></p> 
  <table align="center" border="2"> 
   <tbody> 
    <tr> 
     <td>表达式</td> 
     <td>描述</td> 
    </tr> 
    <tr> 
     <td>nodename</td> 
     <td>选取此节点的所有子节点</td> 
    </tr> 
    <tr> 
     <td>/</td> 
     <td>从当前节点选取直接子节点</td> 
    </tr> 
    <tr> 
     <td>//</td> 
     <td>从当前节点选取子孙节点</td> 
    </tr> 
    <tr> 
     <td>.</td> 
     <td>选取当前节点</td> 
    </tr> 
    <tr> 
     <td>..</td> 
     <td>选取当前节点的父节点</td> 
    </tr> 
    <tr> 
     <td>@</td> 
     <td>选取属性</td> 
    </tr> 
    <tr> 
     <td>*</td> 
     <td>通配符,选择所有元素节点与元素名</td> 
    </tr> 
    <tr> 
     <td>@*</td> 
     <td>选取所有属性</td> 
    </tr> 
    <tr> 
     <td>[@attrib]</td> 
     <td>选取具有给定属性的所有元素</td> 
    </tr> 
    <tr> 
     <td>[@attrib='value']</td> 
     <td>选取给定属性具有给定值的所有元素</td> 
    </tr> 
    <tr> 
     <td>[tag]</td> 
     <td>选取所有具有指定元素的直接子节点</td> 
    </tr> 
    <tr> 
     <td>[tag='text']</td> 
     <td>选取所有具有指定元素并且文本内容是text节点</td> 
    </tr> 
   </tbody> 
  </table> 
  <p> </p> 
  <p><strong>读取 文本 解析节点 </strong>(<span style="color:#f33b45;"><strong> etree 会修复 HTML 文本节点</strong></span> )</p> 
  <pre class="has"><code class="language-python">#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author      : 
# @File        : test.py
# @Software    : PyCharm
# @description : XXX


from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">第一个</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-0"><a href="link5.html">a属性</a>
     </ul>
 </div>
'''

html = etree.HTML(text)  # 初始化生成一个XPath解析对象
result = etree.tostring(html, encoding='utf-8')  # 解析对象输出代码
print(type(html))
print(type(result))
print(result.decode('utf-8'))


'''
执行结果:
<class 'lxml.etree._Element'>
<class 'bytes'>
<html><body><div>
    <ul>
         <li class="item-0"><a href="link1.html">第一个</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-0"><a href="link5.html">a属性</a>
     </li></ul>
 </div>
</body></html>
'''</code></pre> 
  <p><strong>读取 HTML文件 进行解析</strong></p> 
  <pre class="has"><code class="language-python">from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())  # 指定解析器HTMLParser会根据文件修复HTML文件中缺失的如声明信息
result = etree.tostring(html)  # 解析成字节
# result=etree.tostringlist(html) #解析成列表
print(type(html))
print(type(result))
print(result)</code></pre> 
  <p> </p> 
  <p> </p> 
  <h2>节点关系</h2> 
  <p> </p> 
  <h3>(1)父(Parent)</h3> 
  <p style="text-indent:30px;">每个元素以及属性都有一个父。在下面的例子中,book 元素是 title、author、year 以及 price 元素的父:</p> 
  <pre class="has"><code class="language-html"><book>
  <title>Harry Potter
  J K. Rowling
  2005
  29.99

(2)子(Children)

元素节点可有零个、一个或多个子。在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:


  Harry Potter
  J K. Rowling
  2005
  29.99

(3)同胞(Sibling)

拥有相同的父的节点。在下面的例子中,title、author、year 以及 price 元素都是同胞:


  Harry Potter
  J K. Rowling
  2005
  29.99

(4)先辈(Ancestor)

某节点的父、父的父,等等。在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:




  Harry Potter
  J K. Rowling
  2005
  29.99


(5)后代(Descendant)

某个节点的子,子的子,等等。在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素:




  Harry Potter
  J K. Rowling
  2005
  29.99


 

 

选取节点

 

XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。

 

下面列出了最有用的路径表达式:

表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
* 通配符,选择所有元素节点与元素名
@* 选取所有属性
[@attrib] 选取具有给定属性的所有元素
[@attrib='value'] 选取给定属性具有给定值的所有元素
[tag] 选取所有具有指定元素的直接子节点
[tag='text'] 选取所有具有指定元素并且文本内容是text节点

 

实例

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

 

谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。

实例

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式 结果
/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 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

 

选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。

实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。

 

选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

 

XPath 运算符

下面列出了可用在 XPath 表达式中的运算符:( 此表参考来源:http://www.w3school.com.cn/xpath/xpath_operators.asp)

运算符 描述 实例 返回值
| 计算两个节点集 //book | //cd 返回所有拥有 book 和 cd 元素的节点集
+ 加法 6 + 4 10
减法 6 – 4 2
* 乘法 6 * 4 24
div 除法 8 div 4 2
= 等于 price=9.80 如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。
!= 不等于 price!=9.80 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
< 小于 price<9.80 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
<= 小于或等于 price<=9.80 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
> 大于 price>9.80 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
>= 大于或等于 price>=9.80 如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。
or price=9.80 or price=9.70 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。
and price>9.00 and price<9.90 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。
mod 计算除法的余数 5 mod 2 1

 

XPath 函数的高级使用示例:
1.使用 contains() 和 and
    //div[starts-with(@id,'res')]//table[1]//tr//td[2]//a//span[contains(.,'_Test') and contains(.,'KPI')] 
    //div[contains(@id,'in')] ,表示选择id中包含有’in’的div节点
2.text():
    由于一个节点的文本值不属于属性,比如“baidu”,
    所以,用text()函数来匹配节点://a[text()='baidu']
    //span[@id='idHeaderTitleCell' and contains(text(),'QuickStart')]
3.last():
    前面已介绍
4. 使用starts-with()
    //div[starts-with(@id,'in')] ,表示选择以’in’开头的id属性的div节点
    //div[starts-with(@id,'res')]//table//tr//td[2]//table//tr//td//a//span[contains(.,'Developer Tutorial')]
5.not()函数,表示否定。not()函数通常与返回值为true or false的函数组合起来用,
    比如contains(),starts-with()等,但有一种特别情况请注意一下:
    我们要匹配出input节点含有id属性的,写法为://input[@id],
    如果我们要匹配出input节点不含用id属性的,则为://input[not(@id)]
    //input[@name=‘identity’ and not(contains(@class,‘a’))] ,表示匹配出name为identity并且class的值中不包含a的input节点。
6.使用descendant
    //div[starts-with(@id,'res')]//table[1]//tr//td[2]//a//span[contains(.,'QuickStart')]/../../../descendant::img
7.使用ancestor
//div[starts-with(@id,'res')]//table[1]//tr//td[2]//a//span[contains(.,'QuickStart')]/ancestor::div[starts-with(@id,'res')]//table[2]//descendant::a[2]

 

 

Xpath 高级用法

 

scrapy实战2,使用内置的xpath,re 和 css 提取值:https://www.cnblogs.com/regit/p/9629263.html

span 标签 class 属性包含  selectable 字符串://span[contains(@class, 'selectable')]

匹配猫眼 座位数
        //div[@class='seats-wrapper']/div/span[contains(@class,'seat') and not(contains(@class,'empty'))]
    等价于
        //div[@class='seats-wrapper']/div//span[not(contains(//span[contains(@class, 'seat')]/@class, 'empty'))]


./@data-val
//div[contains(@class, "show-list") and @data-index="{0}"]
.//div[@class="show-date"]//span[contains(@class, "date-item")]/text()
.//div[contains(@class, "plist-container")][1]//tbody//tr     xpath 中下标是从 1 开始的
substring-before(substring-after(//script[contains(text(), '/apps/feedlist')]/text(), 'html":"'), '"})')
//div[text()="hello"]/p/text()
//a[@class="movie-name"][1]/text()
string(//a[@class="movie-name"][1])

 

1. 获取父节点属性
    首先选中 href 属性为 link4.html的a节点,然后再获取其父节点,然后再获取其class属性
    result1 = response.xpath('//a[@href="link4.html"]/../@class')
    我们也可以通过parent::来获取父节点
    result2 = response.xpath('//a[@href="link4.html"]/parent::*/@class')
    注意: //a表示html中的所有a节点,他们的href属性有多个,这里[]的作用是属性匹配,找到a的href属性为link4.html的节点
2. 获取节点内部文本
    获取class为item-1的li节点文本,
    result3 = response.xpath('//li[@class="item-0"]/a/text()')
    返回结果为:['first item', 'fifth item']
3. 属性获取
    获取所有li节点下的所有a节点的href属性
    result4 = response.xpath('//li/a/@href')
    返回结果为:['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
4. 按序选择
    result = response.xpath('//li[1]/a/text()')   #选取第一个li节点
    result = response.xpath('//li[last()]/a/text()')   #选取最后一个li节点
    result = response.xpath('//li[position()<3]/a/text()')   #选取位置小于3的li节点,也就是1和2的节点
    result = response.xpath('//li[last()-2]/a/text()')  #选取倒数第三个节点 
5. 节点轴选择
    1)返回第一个li节点的所有祖先节点,包括html,body,div和ul
                result = response.xpath('//li[1]/ancestor::*')     
    2)返回第一个li节点的

祖先节点
                result = response.xpath('//li[1]/ancestor::div')     
    3)返回第一个li节点的所有属性值
                result = response.xpath('//li[1]/attribute::*')     
    4)首先返回第一个li节点的所有子节点,然后加上限定条件,选组href属性为link1.html的a节点
                result = response.xpath('//li[1]/child::a[@href="link1.html"]')     
    5)返回第一个li节点的所有子孙节点,然后加上只要span节点的条件
                result = response.xpath('//li[1]/descendant::span')     
    6)following轴可获得当前节点之后的所有节点,虽然使用了*匹配,但是又加了索引选择,所以只获取第2个后续节点,也就是第2个
  • 节点中的节点
                    result = response.xpath('//li[1]/following::*[2]')     
        7)following-sibling可获取当前节点之后的所有同级节点,也就是后面所有的
  • 节点
                    result = response.xpath('//li[1]/following-sibling::*') 
    6. 属性多值匹配
                   
  • first item
  •      
                    result5 = response.xpath('//li[@class="li"]/a/text()')
        返回值为空,因为这里HTML文本中li节点为class属性有2个值li和li-first,如果还用之前的属性匹配就不行了,需要用contain()函数     
        正确方法如下
                    result5 = response.xpath('//li[contains(@class, "li")]/a/text()')
        contains()方法中,第一个参数为属性名,第二个参数传入属性值,只要此属性名包含所传入的属性值就可完成匹配 
     7. 多属性匹配,这里说一下不用框架的时候,xpath的常规用法
        有时候我们需要多个属性来确定一个节点,那么就需要同时匹配多个属性,可用and来连接    
        from lxml import etree
        text = '''
       
  • first item

  •     '''
        html = etree.HTML(text)
        result6 = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
        print(result)    
        这里的li节点有class和name两个属性,需要用and操作符相连,然后置于中括号内进行条件筛选

     

     

    lxml 用法

    首先我们利用它来解析 HTML 代码,先来一个小例子来感受一下它的基本用法。

    from lxml import etree
    text = '''
    
    '''
    html = etree.HTML(text)
    result = etree.tostring(html)
    print(result)

    首先我们使用 lxml 的 etree 库,然后利用 etree.HTML 初始化,然后我们将其打印出来。

    其中,这里体现了 lxml 的一个非常实用的功能就是自动修正 html 代码,大家应该注意到了,最后一个 li 标签,其实我把尾标签删掉了,是不闭合的。不过,lxml 因为继承了 libxml2 的特性,具有自动修正 HTML 代码的功能。

    所以输出结果是这样的

    
    
    
    

    不仅补全了 li 标签,还添加了 body,html 标签。

     

    文件读取

    除了直接读取字符串,还支持从文件读取内容。比如我们新建一个文件叫做 hello.html,内容为

    利用 parse 方法来读取文件。

    from lxml import etree
    html = etree.parse('hello.html')
    result = etree.tostring(html, pretty_print=True)
    print(result)

    同样可以得到相同的结果。

     

     

    XPath 实例测试

     

    python3解析库 lxml http://www.cnblogs.com/zhangxinqi/p/9210211.html

    依然以上一段程序为例

    (1)获取 所有 的

  • 标签

    from lxml import etree
    html = etree.parse('hello.html')
    print type(html)
    result = html.xpath('//li')
    print result
    print len(result)
    print type(result)
    print type(result[0])

    运行结果

    
    [, , , , ]
    5
    
    

    可见,etree.parse 的类型是 ElementTree,
    通过调用 xpath 以后,得到了一个列表,包含了 5 个

  • 元素,每个元素都是 Element 类型。
    获取所有节点。返回一个列表每个元素都是Element类型,所有节点都包含在其中

    from lxml import etree
    
    html = etree.parse('hello.html', etree.HTMLParser())
    result = html.xpath('//*')  # //代表获取子孙节点,*代表获取所有
    
    print(type(html))
    print(type(result))
    print(result)
    
    # 如要获取li节点,可以使用//后面加上节点名称,然后调用xpath()方法
    html.xpath('//li')   # 获取所有子孙节点的li节点

    (2)获取 子节点

    通过 / 或者 // 即可查找元素的 子节点 或者 子孙节点,如果想选择li节点的所有直接a节点,可以这样使用

    # 通过追加/a选择所有li节点的所有直接a节点,因为//li用于选中所有li节点,/a用于选中li节点的所有直接子节点a
    result=html.xpath('//li/a')  

    (3)获取 父节点

    通过 / 或者 // 可以查找 子节点 或 子孙节点,那么要查找父节点可以使用 .. 来实现也可以使用 parent:: 来获取父节点

    from lxml import etree
    from lxml.etree import HTMLParser
    text='''
    
    '''
    
    html=etree.HTML(text,etree.HTMLParser())
    result=html.xpath('//a[@href="link2.html"]/../@class')
    result1=html.xpath('//a[@href="link2.html"]/parent::*/@class')
    print(result)
    print(result1)
    
    
    '''
    ['item-1']
    ['item-1']
    '''

    (4)属性 匹配

    在选取的时候,我们还可以用 @符号 进行属性过滤。比如,这里如果要选取 class 为 link1.html 的 li 节点,可以这样实现:

    from lxml import etree
    from lxml.etree import HTMLParser
    text='''
    
    '''
    
    html=etree.HTML(text, etree.HTMLParser())
    result=html.xpath('//li[@class="link1.html"]')
    print(result)
    
    # 获取 
  • 标签的所有 class result = html.xpath('//li/@class') print(result)
  • (5)文本 获取

    我们用XPath中的 text() 方法获取节点中的文本

    from lxml import etree
    
    text='''
    
    '''
    
    html=etree.HTML(text,etree.HTMLParser())
    result=html.xpath('//li[@class="item-1"]/a/text()') #获取a节点下的内容
    result1=html.xpath('//li[@class="item-1"]//text()') #获取li下所有子孙节点的内容
    
    print(result)
    print(result1)

    (6)属性 获取

    使用 @符号即可获取节点的属性,如下:获取所有li节点下所有a节点的href属性

    result=html.xpath('//li/a/@href')  #获取a的href属性
    result=html.xpath('//li//@href')   #获取所有li子孙节点的href属性

    (7)属性 多值 匹配

    如果某个属性的值有多个时,我们可以使用 contains() 函数来获取

    from lxml import etree
    
    text1='''
    
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    result=html.xpath('//li[@class="aaa"]/a/text()')
    result1=html.xpath('//li[contains(@class,"aaa")]/a/text()')
    
    print(result)
    print(result1)
    
    #通过第一种方法没有取到值,通过contains()就能精确匹配到节点了
    []
    ['第一个']

    (8)多 属性 匹配

    另外我们还可能遇到一种情况,那就是根据多个属性确定一个节点,这时就需要同时匹配多个属性,此时可用运用and运算符来连接使用:

    from lxml import etree
    
    text1='''
    
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    result=html.xpath('//li[@class="aaa" and @name="fore"]/a/text()')
    result1=html.xpath('//li[contains(@class,"aaa") and @name="fore"]/a/text()')
    
    
    print(result)
    print(result1)
    
    
    #
    ['second item']
    ['second item']

    (9)按序 选择

    有时候,我们在选择的时候某些属性可能同时匹配多个节点,但我们只想要其中的某个节点,如第二个节点或者最后一个节点,这时可以利用中括号引入索引的方法获取特定次序的节点:

    from lxml import etree
    
    text1='''
    
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    
    result=html.xpath('//li[contains(@class,"aaa")]/a/text()') #获取所有li节点下a节点的内容
    result1=html.xpath('//li[1][contains(@class,"aaa")]/a/text()') #获取第一个
    result2=html.xpath('//li[last()][contains(@class,"aaa")]/a/text()') #获取最后一个
    result3=html.xpath('//li[position()>2 and position()<4][contains(@class,"aaa")]/a/text()') #获取第一个
    result4=html.xpath('//li[last()-2][contains(@class,"aaa")]/a/text()') #获取倒数第三个
    
    
    print(result)
    print(result1)
    print(result2)
    print(result3)
    print(result4)
    
    
    #
    ['第一个', '第二个', '第三个', '第四个']
    ['第一个']
    ['第四个']
    ['第三个']
    ['第二个']

    这里使用了last()、position()函数,在XPath中,提供了100多个函数,包括存取、数值、字符串、逻辑、节点、序列等处理功能,它们的具体作用可参考:http://www.w3school.com.cn/xpath/xpath_functions.asp

    (10)节点轴 选择

    XPath提供了很多节点选择方法,包括获取子元素、兄弟元素、父元素、祖先元素等,示例如下:

    from lxml import etree
    
    text1='''
    
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    result=html.xpath('//li[1]/ancestor::*')  #获取所有祖先节点
    result1=html.xpath('//li[1]/ancestor::div')  #获取div祖先节点
    result2=html.xpath('//li[1]/attribute::*')  #获取所有属性值
    result3=html.xpath('//li[1]/child::*')  #获取所有直接子节点
    result4=html.xpath('//li[1]/descendant::a')  #获取所有子孙节点的a节点
    result5=html.xpath('//li[1]/following::*')  #获取当前子节之后的所有节点
    result6=html.xpath('//li[1]/following-sibling::*')  #获取当前节点的所有同级节点
    
    
    #
    [, , , ]
    []
    ['aaa', 'item']
    []
    []
    [, , , , , ]
    [, , ]

    # 获取

  • 标签下 href 为 link1.html 的 标签
    result = html.xpath('//li/a[@href="link1.html"]')
    print result

    # 获取

  • 标签下的所有 标签 (应为是所有,所以使用 // )
    result = html.xpath('//li//span')

    # 获取

  • 标签下的所有 class,不包括

  • result = html.xpath('//li/a//@class')
    print result

    # 获取最后一个

  • 的 href
    result = html.xpath('//li[last()]/a/@href')
    print result

    # 获取倒数第二个元素的内容
    result = html.xpath('//li[last()-1]/a')
    print result[0].text

    # 获取 class 为 bold 的标签名
    result = html.xpath('//*[@class="bold"]')
    print result[0].tag

    以上使用的是XPath轴的用法,更多轴的用法可参考:http://www.w3school.com.cn/xpath/xpath_axes.asp

     

     

     

    案例应用:抓取TIOBE指数前20名排行开发语言

     

    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    # @Author      : 
    # @File        : test_1.py
    # @Software    : PyCharm
    # @description : XXX
    
    
    import requests
    from requests.exceptions import RequestException
    from lxml import etree
    from lxml.etree import ParseError
    import json
    
    
    def one_to_page(html):
        headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                          '(KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'
        }
        try:
            response = requests.get(html, headers=headers)
            body = response.text  # 获取网页内容
            try:
                html = etree.HTML(body, etree.HTMLParser())  # 解析HTML文本内容
                result = html.xpath('//table[contains(@class,"table-top20")]/tbody/tr//text()')  # 获取列表数据
                pos = 0
                for i in range(20):
                    if i == 0:
                        yield result[i:5]
                    else:
                        yield result[pos:pos + 5]  # 返回排名生成器数据
                    pos += 5
            except ParseError as e:
                print(e.position)
        except RequestException as e:
            print('request is error!', e)
    
    
    def write_file(data):  # 将数据重新组合成字典写入文件并输出
        for i in data:
            sul = {
                '2018年6月排行': i[0],
                '2017年6排行': i[1],
                '开发语言': i[2],
                '评级': i[3],
                '变化率': i[4]
            }
            with open('test.txt', 'a', encoding='utf-8') as f:
                f.write(json.dumps(sul, ensure_ascii=False) + '\n')  # 必须格式化数据
                f.close()
            print(sul)
    
    
    def main():
        url = 'https://www.tiobe.com/tiobe-index/'
        data = one_to_page(url)
        write_file(data)
    
    
    if __name__ == '__main__':
        main()
    
    
    '''
    {'2018年6月排行': '1', '2017年6排行': '1', '开发语言': 'Java', '评级': '15.932%', '变化率': '+2.66%'}
    {'2018年6月排行': '2', '2017年6排行': '2', '开发语言': 'C', '评级': '14.282%', '变化率': '+4.12%'}
    {'2018年6月排行': '3', '2017年6排行': '4', '开发语言': 'Python', '评级': '8.376%', '变化率': '+4.60%'}
    {'2018年6月排行': '4', '2017年6排行': '3', '开发语言': 'C++', '评级': '7.562%', '变化率': '+2.84%'}
    {'2018年6月排行': '5', '2017年6排行': '7', '开发语言': 'Visual Basic .NET', '评级': '7.127%', '变化率': '+4.66%'}
    {'2018年6月排行': '6', '2017年6排行': '5', '开发语言': 'C#', '评级': '3.455%', '变化率': '+0.63%'}
    {'2018年6月排行': '7', '2017年6排行': '6', '开发语言': 'JavaScript', '评级': '3.063%', '变化率': '+0.59%'}
    {'2018年6月排行': '8', '2017年6排行': '9', '开发语言': 'PHP', '评级': '2.442%', '变化率': '+0.85%'}
    {'2018年6月排行': '9', '2017年6排行': '-', '开发语言': 'SQL', '评级': '2.184%', '变化率': '+2.18%'}
    {'2018年6月排行': '10', '2017年6排行': '12', '开发语言': 'Objective-C', '评级': '1.477%', '变化率': '-0.02%'}
    {'2018年6月排行': '11', '2017年6排行': '16', '开发语言': 'Delphi/Object Pascal', '评级': '1.396%', '变化率': '+0.00%'}
    {'2018年6月排行': '12', '2017年6排行': '13', '开发语言': 'Assembly language', '评级': '1.371%', '变化率': '-0.10%'}
    {'2018年6月排行': '13', '2017年6排行': '10', '开发语言': 'MATLAB', '评级': '1.283%', '变化率': '-0.29%'}
    {'2018年6月排行': '14', '2017年6排行': '11', '开发语言': 'Swift', '评级': '1.220%', '变化率': '-0.35%'}
    {'2018年6月排行': '15', '2017年6排行': '17', '开发语言': 'Go', '评级': '1.189%', '变化率': '-0.20%'}
    {'2018年6月排行': '16', '2017年6排行': '8', '开发语言': 'R', '评级': '1.111%', '变化率': '-0.80%'}
    {'2018年6月排行': '17', '2017年6排行': '15', '开发语言': 'Ruby', '评级': '1.109%', '变化率': '-0.32%'}
    {'2018年6月排行': '18', '2017年6排行': '14', '开发语言': 'Perl', '评级': '1.013%', '变化率': '-0.42%'}
    {'2018年6月排行': '19', '2017年6排行': '20', '开发语言': 'Visual Basic', '评级': '0.979%', '变化率': '-0.37%'}
    {'2018年6月排行': '20', '2017年6排行': '19', '开发语言': 'PL/SQL', '评级': '0.844%', '变化率': '-0.52%'}
    '''
    
    

     

     

    案例应用:解析 古文网 并打印 诗经 所对应的 URL

     

    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    # @Author      : 
    # @File        : shijing.py
    # @Software    : PyCharm
    # @description : XXX
    
    
    import json
    import traceback
    import requests
    from lxml import etree
    
    """
    step1: 安装 lxml 库。
    step2: from lxml import etree
    step3: selector = etree.HTML(网页源代码)
    step4: selector.xpath(一段神奇的符号)
    """
    
    
    def parse():
        url = 'https://www.gushiwen.org/guwen/shijing.aspx'
        r = requests.get(url)
        if r.status_code == 200:
            selector = etree.HTML(r.text)
            s_all_type_content = selector.xpath('//div[@class="sons"]/div[@class="typecont"]')
            print(len(s_all_type_content))
    
            article_list = list()
            for s_type_content in s_all_type_content:
                book_m1 = s_type_content.xpath('.//strong/text()')[0].encode('utf-8').decode('utf-8')
                s_all_links = s_type_content.xpath('.//span/a')
                article_dict = dict()
                for s_link in s_all_links:
                    link_name = s_link.xpath('./text()')[0].encode('utf-8').decode('utf-8')
                    try:
                        link_href = s_link.xpath('./@href')[0].encode('utf-8').decode('utf-8')
                    except BaseException as e:
                        link_href = None
                    article_dict[link_name] = link_href
                temp = dict()
                temp[book_m1] = article_dict
                article_list.append(temp)
            print(json.dumps(article_list, ensure_ascii=False, indent=4))
    
        else:
            print(r.status_code)
    
    
    if __name__ == '__main__':
        parse()
        pass
    
    
    

     

     

     

    CSS 选择器——cssSelector 定位方式详解

     

    CSS 选择器 参考手册:http://www.w3school.com.cn/cssref/css_selectors.asp
    CSS 选择器 :http://www.runoob.com/cssref/css-selectors.html

    Selenium之CSS Selector定位详解:https://www.bbsmax.com/A/MyJxLGE1Jn/

    css selector

     

    CSS选择器用于选择你想要的元素的样式的模式。

    "CSS"列表示在CSS版本的属性定义(CSS1,CSS2,或对CSS3)。

    选择器 示例 示例说明 CSS
    .class .intro 选择所有class="intro"的元素 1
    #id #firstname 选择所有id="firstname"的元素 1
    * * 选择所有元素 2
    element p 选择所有

    元素

    1
    element,element div,p 选择所有
    元素和

    元素

    1
    element element div p 选择
    元素内的所有

    元素

    1
    element>element div>p 选择所有父级是
    元素的

    元素

    2
    element+element div+p 选择所有紧接着
    元素之后的

    元素

    2
    [attribute] [target] 选择所有带有target属性元素 2
    [attribute=value] [target=-blank] 选择所有使用target="-blank"的元素 2
    [attribute~=value] [title~=flower] 选择标题属性包含单词"flower"的所有元素 2
    [attribute|=language] [lang|=en] 选择 lang 属性以 en 为开头的所有元素 2
    :link a:link 选择所有未访问链接 1
    :visited a:visited 选择所有访问过的链接 1
    :active a:active 选择活动链接 1
    :hover a:hover 选择鼠标在链接上面时 1
    :focus input:focus 选择具有焦点的输入元素 2
    :first-letter p:first-letter 选择每一个

    元素的第一个字母

    1
    :first-line p:first-line 选择每一个

    元素的第一行

    1
    :first-child p:first-child 指定只有当

    元素是其父级的第一个子级的样式。

    2
    :before p:before 在每个

    元素之前插入内容

    2
    :after p:after 在每个

    元素之后插入内容

    2
    :lang(language) p:lang(it) 选择一个lang属性的起始值="it"的所有

    元素

    2
    element1~element2 p~ul 选择p元素之后的每一个ul元素 3
    [attribute^=value] a[src^="https"] 选择每一个src属性的值以"https"开头的元素 3
    [attribute$=value] a[src$=".pdf"] 选择每一个src属性的值以".pdf"结尾的元素 3
    [attribute*=value] a[src*="runoob"] 选择每一个src属性的值包含子字符串"runoob"的元素 3
    :first-of-type p:first-of-type 选择每个p元素是其父级的第一个p元素 3
    :last-of-type p:last-of-type 选择每个p元素是其父级的最后一个p元素 3
    :only-of-type p:only-of-type 选择每个p元素是其父级的唯一p元素 3
    :only-child p:only-child 选择每个p元素是其父级的唯一子元素 3
    :nth-child(n) p:nth-child(2) 选择每个p元素是其父级的第二个子元素 3
    :nth-last-child(n) p:nth-last-child(2) 选择每个p元素的是其父级的第二个子元素,从最后一个子项计数 3
    :nth-of-type(n) p:nth-of-type(2) 选择每个p元素是其父级的第二个p元素 3
    :nth-last-of-type(n) p:nth-last-of-type(2) 选择每个p元素的是其父级的第二个p元素,从最后一个子项计数 3
    :last-child p:last-child 选择每个p元素是其父级的最后一个子级。 3
    :root :root 选择文档的根元素 3
    :empty p:empty 选择每个没有任何子级的p元素(包括文本节点) 3
    :target #news:target 选择当前活动的#news元素(包含该锚名称的点击的URL) 3
    :enabled input:enabled 选择每一个已启用的输入元素 3
    :disabled input:disabled 选择每一个禁用的输入元素 3
    :checked input:checked 选择每个选中的输入元素 3
    :not(selector) :not(p) 选择每个并非p元素的元素 3
    ::selection ::selection 匹配元素中被用户选中或处于高亮状态的部分 3
    :out-of-range :out-of-range 匹配值在指定区间之外的input元素 3
    :in-range :in-range 匹配值在指定区间之内的input元素 3
    :read-write :read-write 用于匹配可读及可写的元素 3
    :read-only :read-only 用于匹配设置 "readonly"(只读) 属性的元素 3
    :optional :optional 用于匹配可选的输入元素 3
    :required :required 用于匹配设置了 "required" 属性的元素 3
    :valid :valid 用于匹配输入值为合法的元素 3
    :invalid :invalid 用于匹配输入值为非法的元素 3

     

    CSS选择器的常见语法:

    Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第6张图片

     

    1.  根据 标签定位 tagName (定位的是一组,多个元素)
            find_element_by_css_selector("div")

    2. 根据 id属性 定位 (注意 id 使用 # 表示)
            find_element_by_css_selector("#eleid")
            find_element_by_css_selector("div#eleid")
            
    3. 根据 className 属性 定位(注意 class 属性 使用.)

            两种方式:前面加上 tag 名称。也可以不加。如果不加 tag 名称时,点不能省略。
            find_element_by_css_selector('.class_value')       
            find_element_by_css_selector("div.eleclass")
            find_element_by_css_selector('tag_name.class_value')

            有的 class_value 比较长,而且中间有空格时,不能把空格原样写进去,那样不能识别。
            这时,空格用点代替,前面要加上 tag_name。
            driver.find_element_by_css_selector('div.panel.panel-email').click()
            #

    This paragraph is a very important warning.


            driver.find_element_by_css_selector('.important')
            driver.find_element_by_css_selector('.important.warning')
            
    4. 根据元素属性定位
            两种方式,可以在前面加上tag名称,也可以不加。
            find_element_by_css_selector("[attri_name='attri_value']")
            find_element_by_css_selector("input[type='password']").send_keys('密码')
            find_element_by_css_selector("[type='password']").send_keys('密码')
        4.1 精确匹配:
            find_element_by_css_selector("div[name=elename]")  #属性名=属性值,精确值匹配
            find_element_by_css_selector("a[href]") #是否存在该属性,判断a元素是否存在href属性

        注意:如果 class属性值 里带空格,用.来代替空格
        4.2 模糊匹配
            find_element_by_css_selector("div[name^=elename]") #从起始位置开始匹配
            find_element_by_css_selector("div[name$=name2]") #从结尾匹配
            find_element_by_css_selector("div[name*=name1]") #从中间匹配,包含
        4.3 多属性匹配
            find_element_by_css_selector("div[type='eletype][value='elevalue']") #同时有多属性
            find_element_by_css_selector("div.eleclsss[name='namevalue'] #选择class属性为eleclass并且name为namevalue的div节点
            find_element_by_css_selector("div[name='elename'][type='eletype']:nth-of-type(1) #选择name为elename并且type为eletype的第1个div节点

    5. 定位子元素 (A>B)
            find_element_by_css_selector("div#eleid>input") #选择id为eleid的div下的所有input节点
            find_element_by_css_selector("div#eleid>input:nth-of-type(4) #选择id为eleid的div下的第4个input节点
            find_element_by_css_selector("div#eleid>nth-child(1)") #选择id为eleid的div下的第一个子节点

    6. 定位后代元素 (A空格B)
            find_element_by_css_selector("div#eleid input") #选择id为eleid的div下的所有的子孙后代的 input 节点
            find_element_by_css_selector("div#eleid>input:nth-of-type(4)+label #选择id为eleid的div下的第4个input节点的相邻的label节点
            find_element_by_css_selector("div#eleid>input:nth-of-type(4)~label #选择id为eleid的div下的第4个input节点之后中的所有label节点

    7. 不是 ( 否 )
            find_element_by_css_selector("div#eleid>*.not(input)") #选择id为eleid的div下的子节点中不为input 的所有子节点
            find_element_by_css_selector("div:not([type='eletype'])") #选择div节点中type不为eletype的所有节点

    8. 包含Bycontent
            find_element_by_css_selector("li:contains('Goa')") #

  • Goat

  •         find_element_by_css_selector("li:not(contains('Goa'))) #
  • Cat
  • 9. by index
            find_element_by_css_selector("li:nth(5)")

    10. 路径法
            两种方式,可以在前面加上 tag 名称,也可以不加。注意它的层级关系使用大于号">"。
            find_element_by_css_selector("form#loginForm>ul>input[type='password']").send_keys('密码')

    Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第7张图片

    高阶:

    Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第8张图片

    Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector_第9张图片

     

     

    基本css选择器

    CSS 选择器中,最常用的选择器 如下:

    选择器 描述 举例
    * 通配选择器,选择所有的元素 *
    选择特定类型的元素,支持基本HTML标签 h1
    . 选择具有特定class的元素。 .class1
    . 特定类型和特定class的交集。(直接将多个选择器连着一起表示交集) h1.class1
    # 选择具有特定id属性值的元素 #id1

     

    属性选择器

    除了最基本的核心选择器外,还有可以 基于属性 的 属性选择器

    选择器 描述 举例
    [attr] 选取定义attr属性的元素,即使该属性没有值 [placeholder]
    [attr="val"] 选取attr属性等于val的元素 [placeholder="请输入关键词"]
    [attr^="val"] 选取attr属性开头为val的元素 [placeholder^="请输入"]
    [attr$="val"] 选取attr属性结尾为val的元素 [placeholder$="关键词"]
    [attr*="val"] 选取attr属性包含val的元素 [placeholder*="入关"]
    [attr~="val"] 选取attr属性包含多个空格分隔的属性,其中一个等于val的元素 [placeholder~="关键词"]
    [attr|="val"] 选取attr属性等于val的元素或第一个属性值等于val的元素 [placeholder|="关键词"]

           

    This paragraph is a very important warning.


            selenium举例: (By.CSS_SELECTOR,'p[class="import warning"]') 
            属性与属性的值需要完全匹配,如上面用p[class='impprtant']就定位不到; 
            部分属性匹配:(By.CSS_SELECTOR,'p[class~="import warning"]'); 
            子串匹配&特定属性匹配: 
            [class^="def"]:选择 class 属性值以 "def" 开头的所有元素 
            [class$="def"]:选择 class 属性值以 "def" 结尾的所有元素 
            [class*="def"]:选择class 属性值中包含子串 "def" 的所有元素 
            [class|="def"]:选择class 属性值等于"def"或以"def-"开头的元素(这个是特定属性匹配)

     

    关系选择器

    有一些选择器是基于层级之间的关系,这类选择器称之为关系选择器

    选择器 描述 举例
      第二个选择器为第一个选择器的后代元素,选取第二个选择器匹配结果 .class1 h1
    > 第二个选择器为第一个选择器的直接子元素,选取第二个选择器匹配结果 .class1 > *
    + 第二个选择器为第一个选择器的兄弟元素,选取第二个选择器的下一兄弟元素 .class1 + [lang]
    ~ 第二个选择器为第一个选择器的兄弟元素,选取第二个选择器的全部兄弟元素 .class1 ~ [lang]

            选择 某个元素 的 后代的元素: 
            selenium举例:(By.CSS_SELECTOR,‘div button’)
            div元素的所有的后代元素中标签为button元素,不管嵌套有多深

            选择 某个元素 的 子代元素: 
            selenium举例:(By.CSS_SELECTOR,‘div > button’)
            div元素的所有的子代元素中标签为button元素(>符号前后的空格可有可无)

            一个元素不好定位时,它的兄长元素很起眼,可以借助兄长来扬名,因此不妨称之为 "弟弟选择器".
            即选择某个元素的弟弟元素(先为兄,后为弟): 
            selenium举例: (By.CSS_SELECTOR,'button + li')
            button与li属于同一父元素,且button与li相邻,选择button下标签为li的元素

     

    联合选择器与反选择器

    利用 联合选择器与反选择器,可以实现 与和或 的关系。

    选择器 描述 举例
    , 属于第一个选择器的元素或者是属于第二个选择器的元素 h1, h2
    :not() 不属于选择器选中的元素 :not(html)

     

    伪元素和伪类选择器

    CSS选择器支持了 伪元素和伪类选择器。

    :active 鼠标点击的元素
    :checked 处于选中状态的元素
    :default 选取默认值的元素
    :disabled 选取处于禁用状态的元素
    :empty 选取没有任何内容的元素
    :enabled 选取处于可用状态的元素
    :first-child 选取元素的第一个子元素
    :first-letter 选取文本的第一个字母
    :first-line 选取文本的第一行
    :focus 选取得到焦点的元素
    :hover 选取鼠标悬停的元素
    :in-range 选取范围之内的元素
    :out-of-range 选取范围之外的元素
    :lang() 选取lang属性为language的元素
    :last-child 选取元素的最后一个子元素

     

     

     

    你可能感兴趣的:(python,爬虫相关)