MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Уеб Scraping със Scrapy и MongoDB

В тази статия ще изградим скрепер за действителен Концерт на свободна практика, при който клиентът иска програма на Python да изстърже данни от Stack Overflow, за да вземе нови въпроси (заглавие на въпроса и URL). След това изпипаните данни трябва да се съхраняват в MongoDB. Струва си да се отбележи, че Stack Overflow има API, който може да се използва за достъп до точните същите данни. Клиентът обаче искаше скрепер, така че той получи скрепер.

Безплатен бонус: Щракнете тук, за да изтеглите скелет на проекта Python + MongoDB с пълен изходен код, който ви показва как да получите достъп до MongoDB от Python.

Актуализации:

  • 01.03.2014 г. – Рефакториран паякът. Благодаря, @kissgyorgy.
  • 18.02.2015 г. – Добавена част 2.
  • 09.06.2015 г. – Актуализирано до най-новата версия на Scrapy и PyMongo – наздраве!

Както винаги, не забравяйте да прегледате условията за ползване/услуга на сайта и спазвайте robots.txt файл, преди да започнете работа по изстъргване. Уверете се, че се придържате към етичните практики за изстъргване, като не наводнявате сайта с многобройни заявки за кратък период от време. Отнасяйте се към всеки сайт, който изстъргвате, сякаш е ваш собствен .


Инсталиране

Нуждаем се от библиотеката Scrapy (v1.0.3) заедно с PyMongo (v3.0.3) за съхраняване на данните в MongoDB. Трябва да инсталирате и MongoDB (не е покрито).


Scrapy

Ако използвате OSX или версия на Linux, инсталирайте Scrapy с pip (с активиран virtualenv):

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

Ако сте на машина с Windows, ще трябва ръчно да инсталирате редица зависимости. Моля, вижте официалната документация за подробни инструкции, както и този видеоклип в YouTube, който създадох.

След като Scrapy е настроен, проверете инсталацията си, като изпълните тази команда в обвивката на Python:

>>>
>>> import scrapy
>>> 

Ако не получите грешка, можете да започнете!



PyMongo

След това инсталирайте PyMongo с pip:

$ pip install pymongo
$ pip freeze > requirements.txt

Сега можем да започнем да създаваме робота.




Проект Scrapy

Нека започнем нов проект на 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

Това създава редица файлове и папки, които включват основен шаблон, за да започнете бързо:

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

Посочете данни

items.py файл се използва за дефиниране на „контейнери“ за съхранение на данните, които планираме да изстържем.

StackItem() класът наследява от Item (docs), който основно има редица предварително дефинирани обекти, които Scrapy вече е изградил за нас:

import scrapy


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

Нека добавим някои елементи, които всъщност искаме да съберем. За всеки въпрос клиентът се нуждае от заглавие и URL. Така че, актуализирайте items.py така:

from scrapy.item import Item, Field


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


Създайте паяка

Създайте файл, наречен stack_spider.py в директорията "паяци". Тук се случва магията – например, където ще кажем на Scrapy как да намери точното данни, които търсим. Както можете да си представите, това е специфично към всяка отделна уеб страница, която искате да изстържете.

Започнете с дефиниране на клас, който се наследява от Spider на Scrapy и след това добавяне на атрибути според нуждите:

from scrapy import Spider


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

Първите няколко променливи се обясняват сами (документи):

  • name определя името на паяка.
  • allowed_domains съдържа базовите URL адреси за разрешените домейни, които паякът може да обхожда.
  • start_urls е списък с URL адреси, от които паякът да започне да обхожда. Всички следващи URL адреси ще започват от данните, които паякът изтегля от URL адресите в start_urls .


Селектори на XPath

След това Scrapy използва XPath селектори за извличане на данни от уебсайт. С други думи, можем да изберем определени части от HTML данните въз основа на даден XPath. Както е посочено в документацията на Scrapy, "XPath е език за избор на възли в XML документи, който може да се използва и с HTML."

Можете лесно да намерите конкретен Xpath с помощта на инструментите за разработчици на Chrome. Просто проверете конкретен HTML елемент, копирайте XPath и след това настройте (ако е необходимо):

Инструментите за разработчици също ви дават възможността да тествате XPath селектори в конзолата на JavaScript, като използвате $x - т.е., $x("//img") :

Отново, ние основно казваме на Scrapy откъде да започне да търси информация въз основа на дефиниран XPath. Нека отидем до сайта на Stack Overflow в Chrome и да намерим селекторите XPath.

Щракнете с десния бутон върху първия въпрос и изберете „Проверка на елемента“:

Сега вземете XPath за <div class="summary"> , //*[@id="question-summary-27624141"]/div[2] , и след това го тествайте в конзолата на JavaScript:

Както можете да разберете, той просто избира този един въпрос. Така че трябва да променим XPath, за да вземем всички въпроси. Някакви идеи? Просто е://div[@class="summary"]/h3 . Какво означава това? По същество този XPath гласи:Вземете всички <h3> елементи, които са дъщерни на <div> който има клас summary . Тествайте този XPath в конзолата на JavaScript.

Забележете как не използваме действителния изход на XPath от Chrome Developer Tools. В повечето случаи изходът е само полезен настрана, което обикновено ви насочва в правилната посока за намиране на работещия XPath.

Сега нека актуализираме stack_spider.py скрипт:

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


Извличане на данните

Все още трябва да анализираме и изстържем данните, които искаме, които попадат в рамките на <div class="summary"><h3> . Отново актуализирайте stack_spider.py така:

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

Заедно с проследяването на стека на Scrapy, трябва да видите изведени 50 заглавия на въпроса и URL адреси. Можете да изобразите изхода в JSON файл с тази малка команда:

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

Вече внедрихме нашия Spider въз основа на нашите данни, които търсим. Сега трябва да съхраняваме изпипаните данни в MongoDB.




Съхранете данните в MongoDB

Всеки път, когато даден елемент бъде върнат, искаме да потвърдим данните и след това да ги добавим към колекция Mongo.

Първоначалната стъпка е да създадем базата данни, която планираме да използваме, за да запазим всички наши обходени данни. Отворете settings.py и посочете конвейера и добавете настройките на базата данни:

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

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

Управление на тръбопровода

Настроихме нашия паяк да обхожда и анализира HTML и сме настроили настройките на нашата база данни. Сега трябва да свържем двете заедно чрез конвейер в pipelines.py .

Свържете се с базата данни

Първо, нека дефинираме метод за действително свързване с базата данни:

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

Тук създаваме клас, MongoDBPipeline() и имаме функция конструктор за инициализиране на класа, като дефинираме настройките на Mongo и след това се свързваме с базата данни.

Обработете данните

След това трябва да дефинираме метод за обработка на анализираните данни:

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

Ние установяваме връзка с базата данни, разопаковаме данните и след това ги запазваме в базата данни. Сега можем да тестваме отново!




Тест

Отново изпълнете следната команда в директорията „стек“:

$ scrapy crawl stack

ЗАБЕЛЕЖКА :Уверете се, че имате демона Mongo - mongod - работи в друг прозорец на терминала.

Ура! Успешно съхранихме нашите обходени данни в базата данни:



Заключение

Това е доста прост пример за използване на Scrapy за обхождане и изстъргване на уеб страница. Действителният проект на свободна практика изисква скриптът да следва връзките за пагинация и да изстърже всяка страница с помощта на CrawlSpider (docs), което е супер лесно за изпълнение. Опитайте да приложите това сами и оставете коментар по-долу с връзката към хранилището на Github за бърз преглед на кода.

Нужда от помощ? Започнете с този скрипт, който е почти завършен. След това вижте част 2 за пълното решение!

Безплатен бонус: Щракнете тук, за да изтеглите скелет на проекта Python + MongoDB с пълен изходен код, който ви показва как да получите достъп до MongoDB от Python.

Можете да изтеглите целия изходен код от хранилището на Github. Коментирайте по-долу с въпроси. Благодаря за четенето!



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Какво представлява грешката на Mongoose Прехвърлянето към ObjectId не бе успешно за стойност XXX на път _id?

  2. връщане на документ с най-нов поддокумент само в mongodb агрегат

  3. В Mongoose как да сортирам по дата? (node.js)

  4. MongoDB:Извличане само на документи, създадени през последните 24 часа?

  5. MongoDB и съставни първични ключове