sql >> Database teknologi >  >> NoSQL >> MongoDB

Webskrabning og gennemgang med Scrapy og MongoDB

Sidste gang implementerede vi en grundlæggende webskraber, der downloadede de seneste spørgsmål fra StackOverflow og gemte resultaterne i MongoDB. I denne artikel udvider vi vores skraber, så den gennemgår sideinddelingslinkene nederst på hver side og skraber spørgsmålene (spørgsmålets titel og webadresse) fra hver side.

Gratis bonus: Klik her for at downloade et Python + MongoDB-projektskelet med fuld kildekode, der viser dig, hvordan du får adgang til MongoDB fra Python.

Opdateringer:

  1. 09/06/2015 - Opdateret til den seneste version af Scrapy (v1.0.3) og PyMongo (v3.0.3) - hej!

Før du påbegynder et skrabejob, skal du gennemgå webstedets vilkår for brugspolitik og respektere robots.txt-filen. Overhold også etisk skrabningspraksis ved ikke at oversvømme et websted med adskillige anmodninger over en kort periode. Behandl ethvert websted, du skraber, som om det var dit eget.

Dette er et samarbejde mellem folkene hos Real Python og György - en Python-entusiast og softwareudvikler, der i øjeblikket arbejder i en big data-virksomhed og søger et nyt job på samme tid. Du kan stille ham spørgsmål på twitter - @kissgyorgy.


Kom godt i gang

Der er to mulige måder at fortsætte fra, hvor vi slap.

Den første er at udvide vores eksisterende Spider ved at udtrække hvert næste sidelink fra svaret i parse_item metode med et xpath-udtryk og kun yield en Request objekt med et tilbagekald til den samme parse_item metode. På denne måde vil scrapy automatisk lave en ny anmodning til det link, vi angiver. Du kan finde flere oplysninger om denne metode i Scrapy-dokumentationen.

Den anden, meget enklere mulighed er at bruge en anden type edderkop - CrawlSpider (link). Det er en udvidet version af den grundlæggende Spider , designet præcis til vores brugssag.



CrawlSpider

Vi bruger det samme Scrapy-projekt fra den sidste tutorial, så tag koden fra repoen, hvis du har brug for den.


Opret Boilerplate

Inden for "stack"-mappen, start med at generere spider-kedelpladen fra crawl skabelon:

$ scrapy genspider stack_crawler stackoverflow.com -t crawl
Created spider 'stack_crawler' using template 'crawl' in module:
  stack.spiders.stack_crawler

Scrapy-projektet skulle nu se sådan ud:

├── scrapy.cfg
└── stack
    ├── __init__.py
    ├── items.py
    ├── pipelines.py
    ├── settings.py
    └── spiders
        ├── __init__.py
        ├── stack_crawler.py
        └── stack_spider.py

Og stack_crawler.py filen skal se sådan ud:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule

from stack.items import StackItem


class StackCrawlerSpider(CrawlSpider):
    name = 'stack_crawler'
    allowed_domains = ['stackoverflow.com']
    start_urls = ['http://www.stackoverflow.com/']

    rules = (
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        i = StackItem()
        #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
        #i['name'] = response.xpath('//div[@id="name"]').extract()
        #i['description'] = response.xpath('//div[@id="description"]').extract()
        return i

Vi skal bare lave et par opdateringer til denne kedel...



Opdater start_urls liste

Tilføj først den første side med spørgsmål til start_urls liste:

start_urls = [
    'http://stackoverflow.com/questions?pagesize=50&sort=newest'
]


Opdater rules liste

Dernæst skal vi fortælle edderkoppen, hvor den kan finde links til næste side ved at tilføje et regulært udtryk til rules attribut:

rules = [
    Rule(LinkExtractor(allow=r'questions\?page=[0-9]&sort=newest'),
         callback='parse_item', follow=True)
]

Scrapy vil nu automatisk anmode om nye sider baseret på disse links og videregive svaret til parse_item metode til at udtrække spørgsmålene og titlerne.

Hvis du er meget opmærksom, begrænser dette regex gennemsøgningen til de første 9 sider, da vi for denne demo ikke ønsker at skrabe alle 176.234 sider!



Opdater parse_item metode

Nu mangler vi bare at skrive, hvordan man parser siderne med xpath, hvilket vi allerede gjorde i den sidste tutorial - så bare kopier det over:

def parse_item(self, response):
    questions = response.xpath('//div[@class="summary"]/h3')

    for question in questions:
        item = StackItem()
        item['url'] = question.xpath(
            'a[@class="question-hyperlink"]/@href').extract()[0]
        item['title'] = question.xpath(
            'a[@class="question-hyperlink"]/text()').extract()[0]
        yield item

Det er det for edderkoppen, men gør ikke start det lige endnu.



Tilføj en downloadforsinkelse

Vi skal være søde over for StackOverflow (og ethvert websted, for den sags skyld) ved at indstille en downloadforsinkelse i settings.py :

DOWNLOAD_DELAY = 5

Dette fortæller Scrapy at vente mindst 5 sekunder mellem hver ny anmodning, den fremsætter. Du satser i bund og grund dig selv. Hvis du ikke gør dette, vil StackOverflow satsbegrænse dig; og hvis du fortsætter med at skrabe siden uden at pålægge en takstgrænse, kan din IP-adresse blive forbudt. Så vær sød - Behandl ethvert websted, du skraber, som om det var dit eget.

Nu er der kun én ting tilbage at gøre - gemme dataene.




MongoDB

Sidste gang downloadede vi kun 50 spørgsmål, men da vi denne gang snupper meget mere data, vil vi gerne undgå at tilføje duplikerede spørgsmål til databasen. Det kan vi gøre ved at bruge en MongoDB-upsert, hvilket betyder, at vi opdaterer spørgsmålstitlen, hvis den allerede er i databasen og indsætter ellers.

Rediger MongoDBPipeline vi definerede tidligere:

class MongoDBPipeline(object):

    def __init__(self):
        connection = pymongo.MongoClient(
            settings['MONGODB_SERVER'],
            settings['MONGODB_PORT']
        )
        db = connection[settings['MONGODB_DB']]
        self.collection = db[settings['MONGODB_COLLECTION']]

    def process_item(self, item, spider):
        for data in item:
            if not data:
                raise DropItem("Missing data!")
        self.collection.update({'url': item['url']}, dict(item), upsert=True)
        log.msg("Question added to MongoDB database!",
                level=log.DEBUG, spider=spider)
        return item

For nemheds skyld optimerede vi ikke forespørgslen og beskæftigede os ikke med indekser, da dette ikke er et produktionsmiljø.



Test

Start edderkoppen!

$ scrapy crawl stack_crawler

Læn dig nu tilbage og se din database fyldes med data!

$ mongo
MongoDB shell version: 3.0.4
> use stackoverflow
switched to db stackoverflow
> db.questions.count()
447
>


Konklusion

Du kan downloade hele kildekoden fra Github-lageret. Kommenter nedenfor med spørgsmål. Skål!

Gratis bonus: Klik her for at downloade et Python + MongoDB-projektskelet med fuld kildekode, der viser dig, hvordan du får adgang til MongoDB fra Python.

Leder du efter mere webskrabning? Sørg for at tjekke Real Python-kurserne ud. Leder du efter at hyre en professionel webskraber? Tjek GoScrape.



  1. Hvordan bruger man Redis masseindsættelse?

  2. MongoDB på Ubuntu starter ikke som en tjeneste, intet i loggen

  3. Er det dårligt at ændre _id-type i MongoDB til heltal?

  4. MongoDB $exp