Использование Regex для поиска HTML-ссылок рядом с ключевыми словами - PullRequest
4 голосов
/ 23 января 2012

Если я ищу ключевое слово "sales" и хочу получить ближайшее "http://www.somewebsite.com", даже если в файле несколько ссылок. Мне нужна ближайшая ссылка, а не первая ссылка. Это означает, что мне нужно найдите ссылку, которая появляется непосредственно перед соответствием ключевого слова.

Это не работает ...

regex = (http|https)://[-A-Za-z0-9./]+.*(?!((http|https)://[-A-Za-z0-9./]+))sales sales

Какой лучший способ найти ссылку, наиболее близкую к ключевому слову?

Ответы [ 4 ]

3 голосов
/ 23 января 2012

Обычно гораздо проще и надежнее использовать анализатор HTML, чем регулярное выражение.

Использование стороннего модуля lxml :

import lxml.html as LH

content = '''<html><a href="http://www.not-this-one.com"></a>
<a href="http://www.somewebsite.com"></a><p>other stuff</p><p>sales</p>
</html>
'''

doc = LH.fromstring(content)    
for url in doc.xpath('''
    //*[contains(text(),"sales")]
    /preceding::*[starts-with(@href,"http")][1]/@href'''):
    print(url)

выходы

http://www.somewebsite.com

Я считаю, что lxml (и XPath) - это удобный способ выразить, какие элементы я ищу. Однако, если установка стороннего модуля невозможна, вы также можете выполнить эту конкретную работу с помощью HTMLParser из стандартной библиотеки:

import HTMLParser
import contextlib

class MyParser(HTMLParser.HTMLParser):
    def __init__(self):
        HTMLParser.HTMLParser.__init__(self)
        self.last_link = None

    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs)
        if 'href' in attrs:
            self.last_link = attrs['href']

content = '''<html><a href="http://www.not-this-one.com"></a>
<a href="http://www.somewebsite.com"></a><p>other stuff</p><p>sales</p>
</html>
'''

idx = content.find('sales')

with contextlib.closing(MyParser()) as parser:
    parser.feed(content[:idx])
    print(parser.last_link)

Относительно XPath, используемого в решении lxml: XPath имеет следующее значение:

 //*                              # Find all elements
   [contains(text(),"sales")]     # whose text content contains "sales"
   /preceding::*                  # search the preceding elements 
     [starts-with(@href,"http")]  # such that it has an href attribute that starts with "http"
       [1]                        # select the first such <a> tag only
         /@href                   # return the value of the href attribute
0 голосов
/ 30 января 2012

Я проверил этот код, и он, кажется, работает ...

def closesturl(keyword, website):
    keylist = []
    urllist = []
    closest = []
    urls = []
    urlregex = "(http|https)://[-A-Za-z0-9\\./]+"
    urlmatches = re.finditer(urlregex, website, re.IGNORECASE)
    keymatches = re.finditer(keyword, website, re.IGNORECASE)
    for n in keymatches:
        keylist.append([n.start(), n.end()])
    if(len(keylist) > 0):
        for m in urlmatches:
            urllist.append([m.start(), m.end()])
    if((len(keylist) > 0) and (len(urllist) > 0)):
        for i in range (0, len(keylist)):
            closest.append([abs(urllist[0][0]-keylist[i][0])])
            urls.append(website[urllist[0][0]:urllist[0][1]])
            if(len(urllist) >= 1):
                for j in range (1, len(urllist)):
                    if((abs(urllist[j][0]-keylist[i][0]) < closest[i])):
                        closest[i] = abs(keylist[i][0]-urllist[j][0])
                        urls[i] = website[urllist[j][0]:urllist[j][1]]
                        if((abs(urllist[j][0]-keylist[i][0]) > closest[i])):
                            break # local minimum / inflection point break from url list                                                      
    if((len(keylist) > 0) and (len(urllist) > 0)):
        return urls #return website[urllist[index[0]][0]:urllist[index[0]][1]]                                                                
    else:
        return ""

    somestring = "hey whats up... http://www.firstlink.com some other test http://www.secondlink.com then mykeyword"
    keyword = "mykeyword"
    print closesturl(keyword, somestring)

Вышеуказанное при запуске показывает ... http://www.secondlink.com.

Если бы кто-то получил идеи о том, как ускорить этот код, это было бы здорово!

Спасибо V $ H.

0 голосов
/ 26 января 2012

Опираясь на то, что предложил математический кофе, вы можете попробовать что-то вроде этого:

import re
myString = "" ## the string you want to search

link_matches = re.finditer('(http|https)://[-A-Za-z0-9./]+',myString,re.IGNORECASE)
sales_matches = re.finditer('sales',myString,re.IGNORECASE)

link_locations = []

for match in link_matches:
    link_locations.append([match.span(),match.group()])

for match in sales_matches:
    match_loc = match.span()
    distances = []
    for link_loc in link_locations:
        if match_loc[0] > link_loc[0][1]: ## if the link is behind your keyword
            ## append the distance between the END of the keyword and the START of the link
            distances.append(match_loc[0] - link_loc[0][1])
        else:
            ## append the distance between the END of the link and the START of the keyword
            distances.append(link_loc[0][0] - match_loc[1])

    for d in range(0,len(distances)-1):
        if distances[d] == min(distances):
            print ("Closest Link: " + link_locations[d][1] + "\n")
            break
0 голосов
/ 23 января 2012

Я не думаю, что вы можете сделать это с помощью одного регулярного выражения (особенно если смотреть перед соответствием ключевого слова), так как нет смысла сравнивать расстояния.

Я думаю, вам лучше всего сделать что-то вроде этого:

  • найти все вхождения sales и получить индекс подстроки, называемый salesIndex
  • найтивсе вхождения https?://[-A-Za-z0-9./]+ и получить индекс подстроки, называемый urlIndex
  • , проходящий через salesIndex.Для каждого местоположения i в salesIndex найдите urlIndex ближайший.

В зависимости от того, как вы хотите судить о «ближайшем», вам может потребоваться получить начало и конечные индексы sales и http... случаев для сравнения.то есть найти конечный индекс URL-адреса, ближайший к начальному индексу текущего вхождения sales, и найти начальный индекс URL-адреса, ближайший к конечному индексу текущего вхождения sales, ивыберите тот, который ближе.

Вы можете использовать matches = re.finditer(pattern,string,re.IGNORECASE), чтобы получить список совпадений, а затем match.span(), чтобы получить начальные / конечные индексы подстрок для каждого match в matches.

...