MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Skrobanie stron internetowych za pomocą Scrapy i MongoDB

W tym artykule zbudujemy skrobak dla rzeczywistego koncert dla freelancerów, podczas którego klient chce, aby program w Pythonie pobierał dane z Stack Overflow, aby pobrać nowe pytania (tytuł pytania i adres URL). Zeskrobane dane powinny być następnie przechowywane w MongoDB. Warto zauważyć, że Stack Overflow ma interfejs API, za pomocą którego można uzyskać dostęp do dokładnego te same dane. Jednak klient chciał skrobaka, więc skrobak jest tym, co otrzymał.

Bezpłatny bonus: Kliknij tutaj, aby pobrać szkielet projektu Python + MongoDB z pełnym kodem źródłowym, który pokazuje, jak uzyskać dostęp do MongoDB z Pythona.

Aktualizacje:

  • 01.03.2014 - Refaktoryzacja pająka. Dzięki, @kissgyorgy.
  • 18.02.2015 – Dodano część 2.
  • 06.09.2015 – Zaktualizowano do najnowszej wersji Scrapy i PyMongo – na zdrowie!

Jak zawsze, zapoznaj się z warunkami użytkowania/usługami witryny i przestrzegaj pliku robots.txt pliku przed rozpoczęciem jakiejkolwiek pracy zgarniania. Upewnij się, że przestrzegasz etycznych praktyk skrobania, nie zalewając witryny licznymi prośbami w krótkim czasie. Traktuj każdą wyszukiwaną witrynę jak własną .


Instalacja

Potrzebujemy biblioteki Scrapy (v1.0.3) wraz z PyMongo (v3.0.3) do przechowywania danych w MongoDB. Musisz również zainstalować MongoDB (nie omówione).


Zadrapanie

Jeśli używasz OSX lub odmiany Linuksa, zainstaluj Scrapy za pomocą pip (z aktywowanym virtualenv):

$ pip install Scrapy==1.0.3
$ pip freeze > requirements.txt

Jeśli jesteś na komputerze z systemem Windows, będziesz musiał ręcznie zainstalować kilka zależności. Zapoznaj się z oficjalną dokumentacją, aby uzyskać szczegółowe instrukcje, a także z tym filmem na YouTube, który stworzyłem.

Po skonfigurowaniu Scrapy zweryfikuj swoją instalację, uruchamiając to polecenie w powłoce Pythona:

>>>
>>> import scrapy
>>> 

Jeśli nie pojawi się błąd, dobrze jest iść!



PyMongo

Następnie zainstaluj PyMongo za pomocą pip:

$ pip install pymongo
$ pip freeze > requirements.txt

Teraz możemy zacząć budować robota.




Scrapy projekt

Zacznijmy nowy projekt Scrapy:

$ scrapy startproject stack
2015-09-05 20:56:40 [scrapy] INFO: Scrapy 1.0.3 started (bot: scrapybot)
2015-09-05 20:56:40 [scrapy] INFO: Optional features available: ssl, http11
2015-09-05 20:56:40 [scrapy] INFO: Overridden settings: {}
New Scrapy project 'stack' created in:
    /stack-spider/stack

You can start your first spider with:
    cd stack
    scrapy genspider example example.com

Spowoduje to utworzenie wielu plików i folderów, które zawierają podstawowy szablon, który umożliwia szybkie rozpoczęcie pracy:

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

Określ dane

items.py plik służy do definiowania „kontenerów” przechowywania danych, które planujemy zeskrobać.

StackItem() klasa dziedziczy z Item (dokumenty), który zasadniczo zawiera wiele predefiniowanych obiektów, które Scrapy już dla nas zbudował:

import scrapy


class StackItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

Dodajmy kilka przedmiotów, które faktycznie chcemy zebrać. Dla każdego pytania klient potrzebuje tytułu i adresu URL. Zaktualizuj items.py jak tak:

from scrapy.item import Item, Field


class StackItem(Item):
    title = Field()
    url = Field()


Stwórz Pająka

Utwórz plik o nazwie stack_spider.py w katalogu „pająki”. To tutaj dzieje się magia – np. kiedy powiemy Scrapy, jak znaleźć dokładnie dane, których szukamy. Jak możesz sobie wyobrazić, jest to konkretne do każdej strony internetowej, którą chcesz zeskrobać.

Zacznij od zdefiniowania klasy, która dziedziczy po Spider Scrapy'ego a następnie dodawanie atrybutów według potrzeb:

from scrapy import Spider


class StackSpider(Spider):
    name = "stack"
    allowed_domains = ["stackoverflow.com"]
    start_urls = [
        "http://stackoverflow.com/questions?pagesize=50&sort=newest",
    ]

Kilka pierwszych zmiennych nie wymaga wyjaśnień (dokumenty):

  • name definiuje nazwę Pająka.
  • allowed_domains zawiera podstawowe adresy URL dozwolonych domen do indeksowania przez pająka.
  • start_urls to lista adresów URL, od których pająk ma zacząć indeksować. Wszystkie kolejne adresy URL będą zaczynały się od danych, które pająk pobiera z adresów URL w start_urls .


Selektory XPath

Następnie Scrapy używa selektorów XPath do wyodrębniania danych ze strony internetowej. Innymi słowy, możemy wybrać określone części danych HTML na podstawie podanej ścieżki XPath. Jak stwierdzono w dokumentacji Scrapy, „XPath to język do wybierania węzłów w dokumentach XML, który może być również używany z HTML”.

Możesz łatwo znaleźć konkretną ścieżkę XPath za pomocą Narzędzi programistycznych Chrome. Po prostu sprawdź określony element HTML, skopiuj XPath, a następnie dostosuj (w razie potrzeby):

Narzędzia programistyczne dają również możliwość testowania selektorów XPath w konsoli JavaScript za pomocą $x - np. $x("//img") :

Ponownie, zasadniczo mówimy Scrapy, od czego zacząć szukać informacji na podstawie zdefiniowanej ścieżki XPath. Przejdźmy do witryny Stack Overflow w Chrome i znajdźmy selektory XPath.

Kliknij prawym przyciskiem myszy pierwsze pytanie i wybierz „Sprawdź element”:

Teraz pobierz ścieżkę XPath dla <div class="summary"> , //*[@id="question-summary-27624141"]/div[2] , a następnie przetestuj go w konsoli JavaScript:

Jak widać, po prostu wybiera ten jeden pytanie. Musimy więc zmienić XPath, aby pobrać wszystkie pytania. Jakieś pomysły? To proste://div[@class="summary"]/h3 . Co to znaczy? Zasadniczo ten XPath stwierdza:Pobierz wszystkie <h3> elementy będące dziećmi <div> który ma klasę summary . Przetestuj tę ścieżkę XPath w konsoli JavaScript.

Zwróć uwagę, że nie używamy rzeczywistych danych wyjściowych XPath z Narzędzi dla programistów Chrome. W większości przypadków dane wyjściowe są tylko pomocne, co generalnie wskazuje właściwy kierunek do znalezienia działającej ścieżki XPath.

Teraz zaktualizujmy stack_spider.py skrypt:

from scrapy import Spider
from scrapy.selector import Selector


class StackSpider(Spider):
    name = "stack"
    allowed_domains = ["stackoverflow.com"]
    start_urls = [
        "http://stackoverflow.com/questions?pagesize=50&sort=newest",
    ]

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


Wyodrębnij dane

Nadal musimy analizować i przeszukiwać żądane dane, które mieszczą się w zakresie <div class="summary"><h3> . Ponownie zaktualizuj stack_spider.py jak tak:

from scrapy import Spider
from scrapy.selector import Selector

from stack.items import StackItem


class StackSpider(Spider):
    name = "stack"
    allowed_domains = ["stackoverflow.com"]
    start_urls = [
        "http://stackoverflow.com/questions?pagesize=50&sort=newest",
    ]

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

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

We are iterating through the `questions` and assigning the `title` and `url` values from the scraped data. Be sure to test out the XPath selectors in the JavaScript Console within Chrome Developer Tools - e.g., `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/text()')` and `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/@href')`.

## Test

Ready for the first test? Simply run the following command within the "stack" directory:

```console
$ scrapy crawl stack

Wraz ze śladem stosu Scrapy powinno zostać wyświetlonych 50 tytułów pytań i adresów URL. Możesz renderować dane wyjściowe do pliku JSON za pomocą tego małego polecenia:

$ scrapy crawl stack -o items.json -t json

Wdrożyliśmy teraz naszego Spidera w oparciu o nasze dane, których szukamy. Teraz musimy przechowywać zeskrobane dane w MongoDB.




Przechowuj dane w MongoDB

Za każdym razem, gdy zwracany jest element, chcemy zweryfikować dane, a następnie dodać je do kolekcji Mongo.

Pierwszym krokiem jest utworzenie bazy danych, której planujemy użyć do zapisania wszystkich naszych przeszukanych danych. Otwórz settings.py i określ potok i dodaj ustawienia bazy danych:

ITEM_PIPELINES = ['stack.pipelines.MongoDBPipeline', ]

MONGODB_SERVER = "localhost"
MONGODB_PORT = 27017
MONGODB_DB = "stackoverflow"
MONGODB_COLLECTION = "questions"

Zarządzanie rurociągami

Skonfigurowaliśmy naszego pająka do indeksowania i analizowania kodu HTML oraz skonfigurowaliśmy ustawienia bazy danych. Teraz musimy połączyć je razem przez potok w pipelines.py .

Połącz z bazą danych

Najpierw zdefiniujmy metodę rzeczywistego połączenia z bazą danych:

import pymongo

from scrapy.conf import settings


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

Tutaj tworzymy klasę MongoDBPipeline() i mamy funkcję konstruktora, która zainicjuje klasę przez zdefiniowanie ustawień Mongo, a następnie połączenie z bazą danych.

Przetwarzaj dane

Następnie musimy zdefiniować metodę przetwarzania przeanalizowanych danych:

import pymongo

from scrapy.conf import settings
from scrapy.exceptions import DropItem
from scrapy import log


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):
        valid = True
        for data in item:
            if not data:
                valid = False
                raise DropItem("Missing {0}!".format(data))
        if valid:
            self.collection.insert(dict(item))
            log.msg("Question added to MongoDB database!",
                    level=log.DEBUG, spider=spider)
        return item

Nawiązujemy połączenie z bazą danych, rozpakowujemy dane, a następnie zapisujemy je w bazie danych. Teraz możemy ponownie przetestować!




Test

Ponownie uruchom następujące polecenie w katalogu „stos”:

$ scrapy crawl stack

UWAGA :Upewnij się, że masz demona Mongo - mongod - działa w innym oknie terminala.

Hurra! Pomyślnie zapisaliśmy nasze zindeksowane dane w bazie danych:



Wniosek

To całkiem prosty przykład wykorzystania Scrapy do indeksowania i skrobania strony internetowej. Rzeczywisty projekt freelancera wymagał od skryptu podążania za linkami stronicowania i skrobania każdej strony za pomocą CrawlSpider (dokumenty), co jest bardzo łatwe do wdrożenia. Spróbuj zaimplementować to na własną rękę i zostaw komentarz poniżej z linkiem do repozytorium Github, aby szybko sprawdzić kod.

Potrzebuję pomocy? Zacznij od tego skryptu, który jest prawie gotowy. Następnie zobacz część 2, aby zobaczyć pełne rozwiązanie!

Bezpłatny bonus: Kliknij tutaj, aby pobrać szkielet projektu Python + MongoDB z pełnym kodem źródłowym, który pokazuje, jak uzyskać dostęp do MongoDB z Pythona.

Możesz pobrać cały kod źródłowy z repozytorium Github. Komentarz poniżej z pytaniami. Dziękujemy za przeczytanie!



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Wyliczenia w MongoDB

  2. Jak wykonać zapytanie z ustawieniami strefy czasowej w Mongodb

  3. Dodaj wiodące zera w SQL

  4. Błąd ECONNREFUSED podczas łączenia się z mongodb z node.js

  5. Obraz Docker mongo „Połączenie odrzucone” z innego kontenera