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

Уеб изстъргване и обхождане със Scrapy и MongoDB

Последния път внедрихме основен уеб скрепер, който изтегли най-новите въпроси от StackOverflow и съхрани резултатите в MongoDB. В тази статия ще разширим нашия скрепер, така че да обхожда връзките за пагинация в долната част на всяка страница и да изтрива въпросите (заглавие на въпроса и URL адрес) от всяка страница.

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

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

  1. 09.06.2015 г. – Актуализирано до най-новата версия на Scrapy (v1.0.3) и PyMongo (v3.0.3) – наздраве!

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

Това е част от сътрудничеството между хората от Real Python и György - ентусиаст на Python и разработчик на софтуер, който в момента работи в компания за големи данни и в същото време търси нова работа. Можете да му задавате въпроси в twitter - @kissgyorgy.


Първи стъпки

Има два възможни начина да продължим оттам, откъдето спряхме.

Първият е да разширим съществуващия ни Spider, като извлечем всяка следваща връзка към страница от отговора в parse_item метод с израз на xpath и просто yield a Request обект с обратно извикване към същия parse_item метод. По този начин scrapy автоматично ще направи нова заявка към посочената от нас връзка. Можете да намерите повече информация за този метод в документацията на Scrapy.

Другият, много по-прост вариант е да използвате различен тип паяк - CrawlSpider (връзка). Това е разширена версия на основния Spider , проектиран точно за нашия случай на употреба.



CawlSpider

Ще използваме същия проект Scrapy от последния урок, така че вземете кода от репо, ако имате нужда от него.


Създайте шаблона

В директорията „stack“ започнете с генериране на шаблона на паяка от crawl шаблон:

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

Проектът Scrapy сега трябва да изглежда така:

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

И stack_crawler.py файл трябва да изглежда така:

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

Просто трябва да направим няколко актуализации на този шаблон...



Актуализирайте start_urls списък

Първо добавете първата страница с въпроси към start_urls списък:

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


Актуализирайте rules списък

След това трябва да кажем на паяка къде може да намери връзките към следващата страница, като добавим регулярен израз към rules атрибут:

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

Scrapy вече автоматично ще изисква нови страници въз основа на тези връзки и ще предава отговора на parse_item метод за извличане на въпросите и заглавията.

Ако обръщате голямо внимание, този регулярен израз ограничава обхождането до първите 9 страници, тъй като за тази демонстрация не искаме да изстъргваме всички 176 234 страници!



Актуализирайте parse_item метод

Сега просто трябва да напишем как да анализираме страниците с xpath, което вече направихме в последния урок - така че просто го копирайте:

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

Това е всичко за паяка, но не започнете още.



Добавяне на забавяне на изтегляне

Трябва да бъдем добри към StackOverflow (и всеки сайт, в този смисъл), като зададем забавяне на изтеглянето в settings.py :

DOWNLOAD_DELAY = 5

Това казва на Scrapy да изчака поне 5 секунди между всяка нова заявка, която прави. Вие по същество ограничавате себе си. Ако не направите това, StackOverflow ще ви ограничи скоростта; и ако продължите да изстъргвате сайта, без да налагате ограничение на скоростта, вашият IP адрес може да бъде забранен. Така че, бъдете мили – Отнасяйте се към всеки сайт, който изстържете, сякаш е ваш собствен.

Сега остава само едно нещо - да съхранявате данните.




MongoDB

Последния път изтеглихме само 50 въпроса, но тъй като този път грабваме много повече данни, искаме да избегнем добавянето на дублиращи се въпроси към базата данни. Можем да направим това, като използваме MongoDB upsert, което означава, че актуализираме заглавието на въпроса, ако то вече е в базата данни, и вмъкваме по друг начин.

Променете MongoDBPipeline дефинирахме по-рано:

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

За простота не оптимизирахме заявката и не се занимавахме с индекси, тъй като това не е производствена среда.



Тест

Стартирайте паяка!

$ scrapy crawl stack_crawler

Сега седнете и гледайте как вашата база данни се пълни с данни!

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


Заключение

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

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

Търсите още уеб изстъргване? Не забравяйте да разгледате курсовете по Real Python. Търсите да наемете професионален уеб скрепер? Вижте GoScrape.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. mongodb:заявка за периода от време между две полета за дата

  2. mongoexport обобщен експорт в csv файл

  3. MongoDB countDocuments()

  4. Етапи на MongoDB Aggregation Framework и тръбопроводи

  5. Намерете и пребройте елементи от колекция с Mongoose