использование lxml для поиска буквального текста URL-ссылок

(Python 3.4.2) Во-первых, я новичок в python — больше, чем новичок, но меньше, чем средний пользователь.

Я пытаюсь отобразить буквальный текст URL-адреса на странице с помощью lxml. Я думаю, что у меня ПОЧТИ есть это, но я что-то упускаю. Я могу получить фактические ссылки, но не их названия.

Пример - отсюда,

<a class="yt-uix-sessionlink yt-uix-tile-link  spf-link  yt-ui-ellipsis yt-ui-ellipsis-2" dir="ltr" aria-describedby="description-id-588180" data-sessionlink="ei=6t2FVJLtEsOWrAbQ24HYAg&amp;ved=CAcQvxs&amp;feature=c4-videos-u" href="/watch?v=I2AcJG4112A&amp;list=UUrtZO4nmCBN4C9ySmi013oA">Zombie on Omegle!</a>

Я хочу получить это:

'Zombie on Omegle!'

(Я сделаю этот HTML-тег более читабельным для вас, ребята)

<a class="yt-uix-sessionlink yt-uix-tile-link  spf-link  yt-ui-ellipsis yt-ui-ellipsis-2"
   dir="ltr" aria-describedby="description-id-588180"
   data-sessionlink="ei=6t2FVJLtEsOWrAbQ24HYAg&amp;ved=CAcQvxs&amp;feature=c4-videos-u"
   href="/watch?v=I2AcJG4112A&amp;list=UUrtZO4nmCBN4C9ySmi013oA">
       Zombie on Omegle!
</a>

Я пытаюсь сделать это со страницы YouTube, и одна из проблем заключается в том, что YouTube не указывает тег или атрибут для заголовков своих ссылок, если это имеет смысл.

Вот что я пробовал:

import lxml.html
from lxml import etree
import urllib

url = 'https://www.youtube.com/user/makemebad35/videos'
response = urllib.request.urlopen(url)
content = response.read()
doc = lxml.html.fromstring(content)
tree = lxml.etree.HTML(content)
parser = etree.HTMLParser()

href_list = tree.xpath('//a/@href')
#Perfect. List of all url's under the 'href' attribute.
href_res = [lxml.etree.tostring(href) for href in href_list]
#^TypeError: Type 'lxml.etree._ElementUnicodeResult' cannot be serialized.

#So I tried extracting the 'a' tag without the attribute 'href'.
a_list = tree.xpath('//a')
a_res = [lxml.etree.tostring(clas) for clas in a_list]
#^This works.

links_fail = lxml.html.find_rel_links(doc,'href')
#^I named it 'links_fail because it doesn't work: the list is empty on output.
#   But the 'links_success' list below works.
urls = doc.xpath('//a/@href')
links_success = [link for link in urls if link.startswith('/watch')]
links_success
#^Out: ['/watch?v=K_yEaIBByFo&list=UUrtZO4nmCBN4C9ySmi013oA', ...]
#Awesome! List of all url's that begin with 'watch?v=..."
#Now only if I could get the titles of the links...

contents = [text.text_content() for text in urls if text.startswith('/watch')]
#^Empty list.

#I thought this paragraph below wouldn't work,
#   but I decided to try it anyway.
texts_fail = doc.xpath('//a/[@href="watch"]')
#^XPathEvalError: Invalid expression
#^Oops, I made a syntax error there. I forgot a '/' before 'watch'.
#    But after correcting it (below), the output is the same.
texts_fail = doc.xpath('//a/[@href="/watch"]')
#^XPathEvalError: Invalid expression
texts_false = doc.xpath('//a/@href="watch"')
texts_false
#^Out: False
#^Typo again. But again, the output is still the same.
texts_false = doc.xpath('//a/@href="/watch"')
texts_false
#^Out: False

target_tag = ''.join(('//a/@class=',
                        '"yt-uix-sessionlink yt-uix-tile-link  spf-link  ',
                        'yt-ui-ellipsis yt-ui-ellipsis-2"'))
texts_html = doc.xpath(target_tag)
#^Out: True
#But YouTube doesn't make attributes for link titles.
texts_tree = tree.xpath(target_tag)
#^Out: True

#I also tried this below, which I found in another stackoverflow question.
#It fails. The error is below.
doc_abs = doc.make_links_absolute(url)
#^Creates empty list, which is why the rest of this paragraph fails.
text = []
text_content = []
notText = []
hasText = []
for each in doc_abs.iter():
    if each.text:
        text.append(each.text)
        hasText.append(each)   # list of elements that has text each.text is true
    text_content.append(each.text_content()) #the text for all elements 
    if each not in hasText:
        notText.append(each)
#AttributeError                            Traceback (most recent call last)
#<ipython-input-215-38c68f560efe> in <module>()
#----> 1 for each in doc_abs.iter():
#      2     if each.text:
#      3         text.append(each.text)
#      4         hasText.append(each)   # list of elements that has text each.text is true
#      5     text_content.append(each.text_content()) #the text for all elements
#
#AttributeError: 'NoneType' object has no attribute 'iter'

У меня нет идей. Кто-нибудь хочет помочь этому питоновому падавану? :П

-----РЕДАКТИРОВАТЬ-----

Я на шаг впереди благодаря theSmallNothing. Эта команда получает текстовые элементы:

doc.xpath('//a/text()')

К сожалению, эта команда возвращает много пробелов и новых строк ('\n') в качестве значений. Я, вероятно, опубликую еще один вопрос позже по этой проблеме. Если я это сделаю, я поставлю ссылку на этот вопрос здесь на случай, если кто-то еще с таким же вопросом окажется здесь.

Как использовать lxml для сопряжения "URL-ссылок" с "именами" ссылок (например, {name: link})


person GreenRaccoon23    schedule 08.12.2014    source источник


Ответы (1)


В вашем примере вы хотите использовать селектор текста в своем запросе xpath:

doc.xpath('//a/text()')

который возвращает текстовый элемент из всех элементов a, которые он может найти.

Чтобы получить href и текст всех элементов a, что, я думаю, вы пытаетесь сделать, вы можете сначала извлечь все элементы a, а затем выполнить итерацию и извлечь href и текст индивидуально.

watch_els = []

els = doc.xpath('//a')
for el in els:
    text = el.xpath("//text()")
    href = el.xpath("//@href")
    #check text and href arrays are not empty...
    if len(href) <= 0 or len(text) <= 0:
        #empty text/href, skip.
        continue

    text = text[0]
    href = href[0]
    if "/watch?" in href:
        #do something with a youtube video link...
        watch_els.append((text, href))
person tsn    schedule 08.12.2014
comment
Да! Спасибо! :D Я думал, что это будет что-то простое. Есть ли способ нажать эту команду, чтобы она отображала только текстовые элементы тегов, атрибуты которых начинаются с «href=/watch...»? Я не могу понять синтаксис регулярного выражения lxml. - person GreenRaccoon23; 08.12.2014
comment
Нет проблем. Это не совсем регулярное выражение, на самом деле это совершенно другое, и вы можете посмотреть его w3schools.com/xpath . Хотя я должен предупредить вас, что попытка выбрать (извлечь элементы) из их содержимого (т. е. начала атрибута href) сложна, вы, вероятно, захотите просто использовать формат, который я использовал выше, с циклом for, за исключением проверки начала URL-адрес (атрибут href) со строковым методом Python startwith docs.python.org/2/library/ stdtypes.html - person tsn; 08.12.2014
comment
Хорошая идея. Звучит просто, но я не могу заставить это работать. Я получаю много значений для doc.xpath('//a'), которые являются просто пробелами или '\n'. - person GreenRaccoon23; 08.12.2014
comment
Вот что я пробовал, если вам интересно. Я, вероятно, опубликую это как еще один вопрос позже, потому что это совсем другая проблема. import re texts = doc.xpath('//a/text()') texts_test = [] texts_test2 = [] for t in texts: texts_test.append(t.strip()) if re.findall('\S', t): texts_test2.append(t) urls = doc.xpath('//a/@href') len(texts_test) #263 len(texts_test2) #44 len(urls) #109 - person GreenRaccoon23; 08.12.2014
comment
Ой! Ха-ха, новые строки не сработали. Да, я обязательно опубликую это как еще один вопрос позже. Я мог бы также опубликовать, что еще я пробовал. from collections import defaultdict links_dic_pre = dict(zip(urls, str(texts))) links_dic = defaultdict() for key, value in links_dic_pre.items(): if key.startswith('https://www.youtube.com/watch'): links_dic[key] = value - person GreenRaccoon23; 08.12.2014
comment
Ха-ха, не знаю, что вы делаете, я обновил ответ, чтобы показать, что я имею в виду, но, как вы сказали, это, вероятно, лучше в другом вопросе. О, и если вы удовлетворены моим ответом, пожалуйста, примите его, мне нравится представитель, и это означает, что вопрос не будет автоматически удален через месяц или около того. - person tsn; 08.12.2014
comment
Я думаю, что в коде есть ошибка: в обеих el.xpath строках // нужно удалить, чтобы работать как задумано - person Sga; 31.05.2018