Первый трюк LXML
В этом разделе основное внимание уделяется использованию XPath и библиотеки синтаксического анализа LXML.
XPath & LXML
XPath (XML Path Language) — это язык, предназначенный для поиска информации в документах XML, который также применим к HTML.
Когда мы сканируем, мы можем использовать XPath для извлечения соответствующей информации.
⚠️【Примечание】LXML должен быть установлен.
Общие правила XPath
| выражение | описывать |
|---|---|
nodename |
Выбрать всех дочерних элементов этого узла |
/ |
Выбрать непосредственные дочерние узлы из текущего узла |
// |
Выберите узлы-потомки от текущего узла |
. |
выбрать текущий узел |
.. |
Выберите родительский узел текущего узла |
@ |
выбрать недвижимость |
мы используем//Правила XPath в начале выбирают все подходящие узлы.
Кроме того, общие операторы см.Операторы XPath.
Импорт HTML
Импорт HTML из строки
Импортируя модуль etree из библиотеки LXML, затем объявляя фрагмент текста HTML, вызывая класс HTML для инициализации, мы успешно создали объект синтаксического анализа XPath.
⚠️【Примечание】Модуль etree может изменять текст HTML.
Вызовите метод tostring() для вывода модифицированного HTML-кода, и результатом будет тип bytes (вы можете использовать метод decode() для преобразования его в тип str)
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">second item</a></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">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))
Импорт HTML из файла
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
получить узел
получить все узлы
Получить все узлы в HTML, используя правила//*:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
Мы получаем список типов элементов.
Получить все указанные теги
Если мы хотим получить все теги li, мы можем изменить правила в html.xpath() в приведенном выше примере на'//li':
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li')
print(result)
Если совпадение не может быть получено, html.xpath вернет[]
получить дочерний узел
Выберите все прямые дочерние элементы узла li, используя правило'//li/a':
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a')
print(result)
Чтобы получить всех потомков узла под ним, вы можете сделать это://li//a
Получить узел с определенным свойством
Используйте символ @ для фильтрации атрибутов. smt[...] это smt с ... ограничениями.
Select href — это узел link4.html, правило'//a[@href="link4.html"]:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]')
print(result)
получить родительский узел
Если мы хотим получить родительский узел приведенного выше примера, а затем получить его атрибут класса:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/../@class')
# 也可以用“节点轴” '//a[@href="link4.html"]/parent::*/@class'
print(result)
Для использования осей узлов см.XPath Axes
получить текст в узле
Метод text() в XPath может получить непосредственный текст в узле (исключая текст в его дочерних узлах).
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/text()')
print(result)
получить атрибут
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a/@href')
print(result)
В приведенном выше методе можно получить только атрибуты только с одним значением, Для следующего:
<li class="li li-first"><a href="link.html">first item</a></li>
Атрибут класса узла li имеет два значения, описанный выше метод не будет работать, мы можем использовать функцию contains():
from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = e#tree.HTML(text)
result = html.xpath('//li[contains(@class, "li")]/a/text()')
print(result)
Здесь вы также можете использовать оператор и для конкатенации:
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,lxml-библиотека.
Второй трюк BeautifulSoup
В этом разделе в основном рассказывается об использовании библиотеки синтаксического анализа BeautifulSoup.
BeautifulSoup
BeautifulSoup предоставляет простые Pythonic-функции для управления навигацией, поиском, изменением деревьев синтаксического анализа и многим другим. Он предоставляет пользователю данные, которые необходимо очистить путем анализа документа. Используя его, мы можем повысить эффективность парсинга.
BeautifulSoup имеет полную официальную китайскую документацию, вы можете просмотреть ееОфициальная документация BeautifulSoup
⚠️【Примечание】 Вам необходимо установить BeautifulSoup и LXML.
BeautifulSoup может использовать различные парсеры, основные из них следующие:
| парсер | инструкции | Преимущество | недостаток |
|---|---|---|---|
| Стандартная библиотека Python | BeautifulSoup(markup, "html.parser") | Встроенная стандартная библиотека Python, умеренная скорость выполнения и высокая отказоустойчивость документации. | Версии до Python 2.7.3 или 3.2.2) имеют плохую отказоустойчивость на китайском языке. |
| HTML-парсер LXML | BeautifulSoup(markup, "lxml") | Высокая скорость, высокая отказоустойчивость документов | Необходимо установить библиотеку языка C |
| XML-парсер LXML | BeautifulSoup(markup, "xml") | Быстрый, единственный парсер, поддерживающий XML | Необходимо установить библиотеку языка C |
| html5lib | BeautifulSoup(markup, "html5lib") | Лучше всего подходит для обеспечения отказоустойчивости, парсинга документов в стиле браузера и создания документов в формате HTML5. | Медленно и не зависит от внешних расширений |
Обычно мы используем синтаксический анализатор LXML для синтаксического анализа следующим образом:
from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello</p>', 'lxml')
print(soup.p.string)
Инициализация объекта BeaufulSoup
Используйте следующий код для импорта HTML, завершения инициализации объекта BeautifulSoup и автоматического исправления (например, закрытия незакрытых тегов).
soup = BeautifulSoup(markup, "lxml") # markup 是 HTML 的 str
После инициализации мы также можем вывести строку для анализа в стандартном формате с отступом:
print(soup.prettify())
Селектор узла
выбрать ярлык
При выборе элемента элемент узла можно выбрать напрямую, вызвав имя узла. Текст внутри узла можно получить, вызвав свойство string.
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = BeautifulSoup(html, 'lxml')
print(soup.title) # <title>The Dormouse's story</title>
print(type(soup.title)) # <class 'bs4.element.Tag'>
print(soup.title.string) # The Dormouse's story
print(soup.head) # <head><title>The Dormouse's story</title></head>
print(soup.p) # <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
вложенный выбор
мы также можем сделатьвложенный выбор, то есть сделать что-то вроде父.子.孙выбор:
print(soup.head.title.string)
Выбор ассоциации
Иногда нам сложно выбрать нужный элемент узла за один шаг.В это время мы можем сначала выбрать элемент узла, а затем использовать его в качестве эталона для выбора его дочерних узлов, родительских узлов, одноуровневых узлов и т. д.
Получатьпотомок узла
После выбора элемента узла, если вы хотите получить его прямоедочерний узелможно назватьcontentsсвойство, вернет список со всеми дочерними узлами, перечисленными по порядку.
Узлы, такие как теги p, могут содержать как текст, так и узлы, и возвращаемый результат будет возвращать их все в виде списка.
soup.p.contents # 注意里面的文字被切成了几部分
'''(result)
[
'Once upon a time ... were\n',
<a class="sister" href="..." id="link1"><!-- Elsie --></a>,
',\n',
<a class="sister" href="..." id="link2">Lacie</a>,
' and\n',
<a class="sister" href="..." id="link3">Tillie</a>,
';\nand ... well.'
]
'''
При этом запросдочерний узел, мы также можем использоватьchildrenсвойство, он вернет объект list_iterator, который при преобразовании в список совпадает с содержимым:
>>> s.p.children
<list_iterator object at 0x109d6a8d0>
>>> a = list(soup.p.children)
>>> b = soup.p.contents
>>> a == b
True
Мы можем вывести дочерние узлы один за другим:
for i, child in enumerate(soup.p.children):
print(i, child)
получить всепотомок узла(все подчиненные узлы), вы можете вызвать свойство потомков, потомки будут рекурсивно запрашивать все дочерние узлы (сначала в глубину) и получать все дочерние узлы, а возвращаемый результат будет<generator object Tag.descendants at 0x109d297c8>:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.descendants)
for i, d in enumerate(soup.p.descendants):
print(i, d)
Получить родительские и предковые узлы
Если вы хотите получить родительский узел элемента узла, вы можете вызватьparentсвойство, которое возвращает узел:
>>> soup.span.parent
# 结果是 <p>...</p>
Если мы хотим получить все узлы-предки (ищем слой за слоем до всего html), мы можем вызватьparentsсвойство, которое возвращает генератор:
>>> soup.span.parents
<generator object PageElement.parents at 0x109d29ed0>
>>> list(soup.span.parents)
# 结果是 [<p>...</p>, <div>...</div>, <body>...</body>, <html>...</html>]
⚠️【Примечание】Отец — родитель, предок — родители
Получить одноуровневые узлы
Чтобы получить узлы одного уровня, то есть одноуровневые узлы, мы можем вызвать четыре разных свойства, и их функции различны:
- next_sibling: получить узелследующийРодственный узел, узел возврата.
- предыдущий_sibling: получитьвверх одинРодственный узел, узел возврата.
- next_siblings: получитьвниз всеРодственный узел, возвращает генератор.
- предыдущая_siblings: получитьвверх всеРодственный узел, возвращает генератор.
>>> from bs4 import BeautifulSoup
>>> html = """
... <html>
... <body>
... <p class="story">
... Once upon a time there were three little sisters; and their names were
... <a href="http://example.com/elsie" class="sister" id="link1">
... <span>Elsie</span>
... </a>
... Hello
... <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
... and
... <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
... and they lived at the bottom of a well.
... </p>
... """
>>> soup = BeautifulSoup(html, 'lxml')
>>> soup.a
<a class="sister" href="http://example.com/elsie" id="link1">
<span>Elsie</span>
</a>
>>> soup.a.next_sibling
'\n Hello\n '
>>> soup.a.previous_sibling
'\n Once upon a time there were three little sisters; and their names were\n '
>>> soup.a.next_siblings
<generator object PageElement.next_siblings at 0x1110e57c8>
>>> soup.a.previous_siblings
<generator object PageElement.previous_siblings at 0x1110e5de0>
>>> for i in soup.a.previous_siblings:
... print(i)
...
Once upon a time there were three little sisters; and their names were
>>> for i in soup.a.next_siblings:
... print(i)
...
Hello
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
and they lived at the bottom of a well.
>>>
селектор метода
Когда иногда сложно использовать селектор узла для прямого поиска нужного узла, мы можем использовать find_all(), find() и другие методы для гибкого запроса, передавая соответствующие параметры, чтобы получить желаемый узел, а затем выбрать его с помощью Вы можете легко получить необходимую информацию.
find()
find() передает некоторые атрибуты или текст, чтобы получить элементы, соответствующие условию, и возвращает первый соответствующий элемент.
find(name , attrs , recursive , text , **kwargs)
Примеры использования следующие:
from bs4 import BeautifulSoup
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
soup = BeautifulSoup(html, 'lxml')
print(soup.find(name='ul')
print(soup.find(attrs={'class': 'element'}))
print(soup.find(text=re.compile('.*?o.*?', re.S))) # 结果会返回匹配正则表达式的第一个节点的文本(结果不是节点)
findall()
find_all похож на find, но find_all запрашивает все совпадающие элементы и возвращает список всех совпадающих элементов.
Более
Существуют также поиски, такие как find_parents(), find_next_siblings(), find_previous_siblings() и т. д. Основные способы использования аналогичны, но диапазон поиска отличается.Документация.
Селекторы CSS
BeautifulSoup также предоставляет селекторы CSS. Чтобы использовать селекторы CSS, вам нужно только вызвать метод select() и передать соответствующий селектор CSS.Возвращаемый результат — это список узлов, соответствующих селектору CSS:
from bs4 import BeautifulSoup
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
soup = BeautifulSoup(html, 'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))
В ПОРЯДКЕ
Получить полный ярлык
Чтобы получить полный HTML-код тега, просто напишите его селектор узла:
soup.title
получить тип тега
Используйте свойство name, чтобы получить тип узла (p, a, title, pre и т. д.):
print(soup.title.name)
Получить содержимое ярлыка
Как мы уже говорили, текст внутри узла можно получить, вызвав строковое свойство:
soup.title.string
⚠️【Примечание】Если ярлык содержит другие ярлыки,.stringне работает, он вернет None:
>>> from bs4 import BeautifulSoup
>>> html = '<p>Foo<a href="#None">Bar</a></p>'
>>> soup = BeautifulSoup(html, 'lxml')
>>> print(soup.p.string)
None
Чтобы получить содержимое, вы также можете использовать метод узла get_text():
soup.p.get_text()
Используя get_text, вы можете получить весь текст под меткой, включая ее дочерние узлы:
>>> from bs4 import BeautifulSoup
>>> html = '<p>Foo<a href="#None">Bar</a></p>'
>>> soup = BeautifulSoup(html, 'lxml')
>>> print(soup.p.string)
None
>>> print(soup.p.get_text())
FooBar
получить атрибут
Каждый узел может иметь несколько атрибутов, таких как идентификатор, класс, мы можем вызвать attrs, чтобы получить все атрибуты, а затем мы можем использовать метод значения словаря (квадратные скобки плюс имя атрибута или вызвать егоget()метод) для получения определенного свойства:
print(soup.p.attrs)
print(soup.p.attrs['name'])
'''(results)
{'class': ['title'], 'name': 'dromouse'}
dromouse
'''
Вы также можете напрямую использовать квадратные скобки и имена свойств:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
for ul in soup.select('ul'):
print(ul['id'])
print(ul.attrs['id'])
# 循环体的两行代码等效
Третий трюк PyQuery
В этом разделе в основном рассказывается об использовании библиотеки синтаксического анализа PyQuery.
PyQuery
pyquery: a jquery-like library for python
Использование PyQuery очень похоже на jQuery. Если вы старый клиент, использующий jQuery, попробуйте.
⚠️【Примечание】Необходимо установить PyQuery.
инициализация
PyQuery можно инициализировать с различными формами источников данных, таких как строки HTML, исходные URL-адреса, имена локальных файлов и т. д.
Инициализация строки
from pyquery import PyQuery as pq
html = '''
<h1>Header</h1>
<p>Something</p>
<p>Other thing</p>
<div>
<p>In div</p>
</div>
'''
doc = pq(html) # 传入HTML字符串
print(doc('p')) # 传入CSS选择器
'''(results)
<p>Something</p>
<p>Other thing</p>
<p>In div</p>
'''
инициализация URL-адреса
from pyquery import PyQuery as pq
doc = pq(url='http://www.baidu.com', encoding='utf-8') # 这里不写encoding可能中文乱码
print(doc('title'))
'''(result)
<title>百度一下,你就知道</title>
'''
Селекторы CSS
смотрите подробностиТаблица выбора CSS.
найти узел
- найтисындля узла
children('css-selector')метод, все параметры пусты. - найтипотомкидля узла
find('css-selector')метод,Параметры не могут быть пустыми! - найтиотецдля узла
parent('css-selector')метод, все параметры пусты. - найтипредокдля узла
parents('css-selector')метод, все параметры пусты. - найтиродной братдля узла
siblings('css-selector')метод, все параметры пусты.
>>> p = doc('div')
>>> p
[<div#wrapper>, <div#head>, <div.head_wrapper>, <div.s_form>, <div.s_form_wrapper>, <div#lg>, <div#u1>, <div#ftCon>, <div#ftConw>]
>>> type(p)
<class 'pyquery.pyquery.PyQuery'>
>>> p.find('#head')
[<div#head>]
>>> print(p.find('#head'))
<div id="head"> ... </div>
траверс
Результаты, выбранные с помощью PyQuery, можно повторять:
>>> for i in p.parent():
... print(i, type(i))
...
<Element a at 0x1055332c8> <class 'lxml.html.HtmlElement'>
<Element a at 0x105533368> <class 'lxml.html.HtmlElement'>
<Element a at 0x105533458> <class 'lxml.html.HtmlElement'>
Обратите внимание, что это элемент lxml, и он должен обрабатываться методом lxml.
получение информации
attr()получить атрибут
a = doc('a')
print(a.attr('href'))
attr() должно быть передано имя атрибута для выбора. Если объект содержит несколько узлов, вызов attr() для объекта вернет только соответствующий результат первого объекта. Чтобы вернуться, каждый должен пройти траверс.
text()получить текст
a = doc('a')
a.text()
Это выведет результат всех текстовых соединений, содержащих узлы.
Работа узла
PyQuery также может манипулировать узлами, что не является предметом рассмотрения в этой статье и не будет рассматриваться снова.