Справка по регулярным выражениям Ruby с использованием соответствия для извлечения фрагментов HTML-документа - PullRequest
0 голосов
/ 02 апреля 2009

У меня есть HTML-документ этого формата:

<tr><td colspan="4"><span class="fullName">Bill Gussio</span></td></tr>
    <tr>
        <td class="sectionHeader">Contact</td>
        <td class="sectionHeader">Phone</td>
        <td class="sectionHeader">Home</td>
        <td class="sectionHeader">Work</td>
    </tr>
    <tr valign="top">
        <td class="sectionContent"><span>Screen Name:</span> <span>bhjiggy</span><br><span>Email 1:</span> <span>wmgussio@erols.com</span></td>
        <td class="sectionContent"><span>Mobile: </span><span>2404173223</span></td>
        <td class="sectionContent"><span>NY</span><br><span>New York</span><br><span>78642</span></td>
        <td class="sectionContent"><span>MD</span><br><span>Owings Mills</span><br><span>21093</span></td>
    </tr>

    <tr><td colspan="4"><hr class="contactSeparator"></td></tr>

    <tr><td colspan="4"><span class="fullName">Eddie Osefo</span></td></tr>
    <tr>
        <td class="sectionHeader">Contact</td>
        <td class="sectionHeader">Phone</td>
        <td class="sectionHeader">Home</td>
        <td class="sectionHeader">Work</td>
    </tr>
    <tr valign="top">
        <td class="sectionContent"><span>Screen Name:</span> <span>eddieOS</span><br><span>Email 1:</span> <span>osefo@wam.umd.edu</span></td>
        <td class="sectionContent"></td>
        <td class="sectionContent"><span></span></td>
        <td class="sectionContent"><span></span></td>
    </tr>

    <tr><td colspan="4"><hr class="contactSeparator"></td></tr>

Таким образом, он чередуется - порция контактной информации и затем «разделитель контактов». Я хочу получить контактную информацию, поэтому моим первым препятствием является захват фрагментов между контактными разделителями. Я уже разобрался с регулярным выражением, используя rubular. Это:

/<tr><td colspan="4"><span class="fullName">((.|\s)*?)<hr class="contactSeparator">/

Вы можете проверить рубуляр, чтобы убедиться, что это изолирует куски.

Однако моя большая проблема в том, что у меня проблемы с кодом ruby. Я использую встроенную функцию сопоставления и делаю распечатки, но не получаю ожидаемых результатов. Вот код:

page = agent.get uri.to_s    
chunks = page.body.match(/<tr><td colspan="4"><span class="fullName">((.|\s)*?)<hr class="contactSeparator">/).captures

chunks.each do |chunk|
   puts "new chunk: " + chunk.inspect
end

Обратите внимание, что page.body - это просто тело HTML-документа, захваченного Mechanize. HTML-документ намного больше, но имеет этот формат. Итак, неожиданный вывод ниже:

new chunk: "Bill Gussio</span></td></tr>\r\n\t<tr>\r\n\t\t<td class=\"sectionHeader\">Contact</td>\r\n\t\t<td class=\"sectionHeader\">Phone</td>\r\n\t\t<td class=\"sectionHeader\">Home</td>\r\n\t\t<td class=\"sectionHeader\">Work</td>\r\n\t</tr>\r\n\t<tr valign=\"top\">\r\n\t\t<td class=\"sectionContent\"><span>Screen Name:</span> <span>bhjiggy</span><br><span>Email 1:</span> <span>wmgussio@erols.com</span></td>\r\n\t\t<td class=\"sectionContent\"><span>Mobile: </span><span>2404173223</span></td>\r\n\t\t<td class=\"sectionContent\"><span>NY</span><br><span>New York</span><br><span>78642</span></td>\r\n\t\t<td class=\"sectionContent\"><span>MD</span><br><span>Owings Mills</span><br><span>21093</span></td>\r\n\t</tr>\r\n\t\r\n\t<tr><td colspan=\"4\">"
new chunk: ">"

Здесь есть 2 сюрприза для меня:

1) Не существует двух совпадений, содержащих куски контактной информации, хотя в рубрикаторе я убедился, что эти куски следует извлечь.

2) Все \ r \ n \ t (переводы строк, табуляции и т. Д.) Отображаются в матчах.

Кто-нибудь может увидеть проблему здесь?

В качестве альтернативы, если кто-нибудь знает о хорошем импортере бесплатных контактов AOL, это было бы здорово. Я пользуюсь черной книгой, но она не работает для меня на AOL, и я пытаюсь это исправить. К сожалению, у AOL еще нет API контактов.

Спасибо!

Ответы [ 4 ]

4 голосов
/ 02 апреля 2009
3 голосов
/ 02 апреля 2009

Использование парсера HTML, такого как hpricot, избавит вас от множества головных болей:)

sudo gem install hpricot

Это в основном написано на C, так что это также быстро

Вот как это использовать:

http://wiki.github.com/why/hpricot/hpricot-basics

3 голосов
/ 02 апреля 2009

Если вы просто извлекаете информацию из XML, может быть проще использовать что-то кроме регулярных выражений. XPath - это хороший инструмент для извлечения информации из XML. Я полагаю, что есть некоторые библиотеки, доступные для Ruby, которые поддерживают XPath, возможно, попробуйте REXML:

0 голосов
/ 02 апреля 2009

Это код, который анализирует этот HTML. Не стесняйтесь предложить что-то лучшее:

contacts = []
    email, mobile = "",""

    names = page.search("//span[@class='fullName']")

    # Every contact has a fullName node, so for each fullName node, we grab the chunk of contact info
    names.each do |n|

      # next_sibling.next_sibling skips:
      # <tr>
      #   <td class=\"sectionHeader\">Contact</td>
      #   <td class=\"sectionHeader\">Phone</td>
      #   <td class=\"sectionHeader\">Home</td>
      #   <td class=\"sectionHeader\">Work</td>
      # </tr>
      # to give us the actual chunk of contact information
      # then taking the children of that chunk gives us rows of contact info
      contact_info_rows = n.parent.parent.next_sibling.next_sibling.children

      # Iterate through the rows of contact info
      contact_info_rows.each do |row|

        # Iterate through the contact info in each row
        row.children.each do |info|
          # Get Email. There are two ".next_siblings" because space after "Email 1" element is processed as a sibling
          if info.content.strip == "Email 1:" then email = info.next_sibling.next_sibling.content.strip end

          # If the contact info has a screen name but no email, use screenname@aol.com
          if (info.content.strip == "Screen Name:" && email == "") then email = info.next_sibling.next_sibling.content.strip + "@aol.com" end

          # Get Mobile #'s
          if info.content.strip == "Mobile:" then mobile = info.next_sibling.content.strip end

          # Maybe we can try and get zips later.  Right now the zip field can look like the street address field
          # so we can not tell the difference.  There is no label node
          #zip_match = /\A\D*(\d{5})-?\d{4}\D*\z/i.match(info.content.strip) 
          #zip_match = /\A\D*(\d{5})[^\d-]*\z/i.match(info.content.strip)     
        end  

      end

      contacts << { :name => n.content, :email => email, :mobile => mobile }

      # clear variables
      email, mobile = "", ""
    end
...