Ruby aide régulière d'expression en utilisant correspondance pour extraire des morceaux de doc html

StackOverflow https://stackoverflow.com/questions/708350

Question

J'ai un document HTML de ce format:

<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>

Il alterne - morceau de coordonnées et puis un « séparateur de contact ». Je veux saisir les informations de contact donc mon premier obstacle est de saisir les morceaux entre le séparateur de contact. Je l'ai déjà compris l'expression régulière en utilisant Rubular. Il est:

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

Vous pouvez vérifier Rubular pour vérifier que cela permet d'isoler des morceaux.

Cependant, mon gros problème est que je ne parviens pas à le code Ruby. J'utilise la fonction intégrée de correspondance et faire des impressions, mais ne pas les résultats que j'attends. Voici le code:

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

Notez que page.body est juste le corps du document html attrapée par Mechanize. Le document HTML est beaucoup plus grand, mais a ce format. Ainsi, la sortie inattendue est ci-dessous:

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: ">"

Il y a 2 surprises ici pour moi:

1) Il n'y a pas 2 matchs qui contiennent les morceaux de coordonnées, même si sur Rubular J'ai vérifié que ces morceaux doivent être extraits.

2) Tous les \ r \ n \ t (interlignes, onglets, etc.) font leur apparition dans les matches.

Quelqu'un peut-il voir la question ici?

Par ailleurs, si quelqu'un connaît un bon importateur sans contacts AOL, ce serait génial. J'utilise Blackbook, mais il échoue après pour moi sur AOL et je tente de le corriger. Malheureusement, AOL n'a pas encore de contacts API.

Merci!

Était-ce utile?

La solution 4

Ceci est le code qui parse que HTML. Ne hésitez pas à suggérer quelque chose de mieux:

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

Autres conseils

Si vous êtes juste extraire des informations sur XML, il pourrait être plus facile à utiliser autre chose que des expressions régulières. XPath est un bon outil pour extraire les informations de XML. Je crois qu'il ya des bibliothèques disponibles pour Ruby qui prennent en charge XPath, peut-être essayer REXML:

Utilisez un analyseur HTML tel que hpricot vous permettra d'économiser beaucoup de maux de tête:)

  

sudo gem installer hpricot

Il est surtout écrit en C, il est donc rapide et

Voici comment l'utiliser:

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top