2017-07-23 3 views
2

J'ai parcouru beaucoup de htmls (avec un contenu similaire) à partir de nombreux sites de Scrapy, alors que la structure dom est différente.Est-il possible de trouver les nœuds avec la même structure dom

Par exemple, l'un des sites utilisent la structure suivante:

<div class="post"> 
    <section class='content'> 
     Content1 
    </section> 

    <section class="panel"> 
    </section> 
</div> 
<div class="post"> 
    <section class='content'> 
     Conent2 
    </section> 

    <section class="panel"> 
    </section> 
</div> 

Et je veux extraire les données Content et Content2.

Alors un autre site peut utiliser la structure comme ceci:

<article class="entry"> 
    <section class='title'> 
     Content3 
    </section> 
</article> 
<article class="entry"> 
    <section class='title'> 
     Conent4 
    </section> 
</article> 

Et je veux extraire les données Content3 et Content4.

Alors que la solution la plus simple consiste à marquer les données xpath une par une pour tous les sites. Ce serait un travail fastidieux.

Je me demande si la structure peut être extraite automatiquement. En fait, je ai juste besoin d'être situé au nœud racine répétée (div.post et article.entry dans l'exemple ci-dessus), puis je peux extraire les données avec certaines règles.

Est-ce possible?

BTW, je ne suis pas exactement sûr du nom de ce genre d'algorithmes, donc l'étiquette de ce post peut-être mal, n'hésitez pas à le modifier si c'est vrai.

+1

Êtes-vous à la recherche d'une solution qui couvrirait 2 sites spécifiques ou un nombre inconnu de sites? – Granitosaurus

+0

Sur la base de ces deux exemples, il semble que votre contenu se trouve toujours dans un '

'. Si cela est vrai pour tous les sites Web que vous êtes en train de gratter, vous pouvez utiliser 'response.xpath ('// section [@ class =" title "]/text()'). Extract()'. – Casper

+0

@Granitosaurus Oui, mais les sites ont le même type de contenu avec une structure dom différente. – hguser

Répondre

3

Vous devez connaître au moins quelques modèles communs pour être en mesure de formuler des règles d'extraction déterministes. La solution ci-dessous est très primitive et nullement optimale, mais il peut vous aider:

# -*- coding: utf-8 -*- 
import re 

import bs4 
from bs4 import element 
import scrapy 


class ExampleSpider(scrapy.Spider): 
    name = "example" 
    start_urls = ['http://quotes.toscrape.com/'] 

    def parse(self, response): 
     min_occurs = 5 
     max_occurs = 1000 
     min_depth = 7 
     max_depth = 7 
     pattern = re.compile('^/html/body/.*/(span|div)$') 
     extract_content = lambda e: e.css('::text').extract_first() 
     #extract_content = lambda e: ' '.join(e.css('*::text').extract()) 

     doc = bs4.BeautifulSoup(response.body, 'html.parser') 

     paths = {} 
     self._walk(doc, '', paths) 
     paths = self._filter(paths, pattern, min_depth, max_depth, 
          min_occurs, max_occurs) 

     for path in paths.keys(): 
      for e in response.xpath(path): 
       yield {'content': extract_content(e)} 

    def _walk(self, doc, parent, paths): 
     for tag in doc.children: 
      if isinstance(tag, element.Tag): 
       path = parent + '/' + tag.name 
       paths[path] = paths.get(path, 0) + 1 
       self._walk(tag, path, paths) 

    def _filter(self, paths, pattern, min_depth, max_depth, min_occurs, max_occurs): 
     return dict((path, count) for path, count in paths.items() 
         if pattern.match(path) and 
           min_depth <= path.count('/') <= max_depth and 
           min_occurs <= count <= max_occurs) 

Il fonctionne comme ceci:

  1. Explorez document HTML et construire dictionnaire de tous les chemins d'éléments dans le document ensemble avec leurs occurrences.
  2. Filtrez ces chemins en fonction de vos règles générales que vous indiquez à partir de vos pages Web.
  3. Extraire le contenu de ces chemins filtrés en utilisant une logique d'extraction commune.

Pour construire le dictionnaire de chemins, je marche simplement à travers le document en utilisant BeautifulSoup et compte l'occurrence de chaque chemin d'élément. Cela peut plus tard être utilisé dans la tâche de filtrage pour ne conserver que les chemins les plus répugnants.

Ensuite, je filtre les chemins en fonction de certaines règles de base. Pour chemin à conserver, il doit:

  • au moins min_occurs ÉCLATENT et au plus max_occurs fois.
  • A une longueur d'au moins min_depth et au plus max_depth.
  • Correspond à pattern.

D'autres règles peuvent être ajoutées de manière similaire.

La dernière partie parcourt les chemins qui vous ont quittés après le filtrage et extrait le contenu des éléments à l'aide d'une logique commune définie à l'aide de extract_content.

Si vos pages Web sont plutôt simples et que vous pourriez déduire de telles règles, cela pourrait fonctionner. Sinon, vous devriez regarder une sorte de tâche d'apprentissage automatique, je suppose.