Поисковый робот Python - scrapy - Crawling Douban Movie TOP250

Python

0. Предисловие

Я новичок в краулерах. После периода практики я написал несколько простых краулеров. В Интернете есть много примеров краулеров для сканирования фильмов Douban, но они очень простые. Большинство из них только вводят страницу запроса и часть разбора , Для начинающих, я надеюсь, что есть более полный пример. Так что я нашел много примеров и статей и интегрировал их вместе.На основе существующего сканера Douban я добавил некоторый контент, который является относительно полным контентом. В основном это включает в себя создание проекта, страницу запроса, анализ xpath, автоматическое перелистывание страниц, вывод данных, обработку кодирования и так далее. .

1. Создайте проект

Выполните следующую команду, чтобы создать проект сканера scrapy.

scrapy startproject spider_douban

После выполнения команды происходит установлениеspider_doubanпапка, структура каталогов выглядит следующим образом:

.
├── scrapy.cfg
└── spider_douban
    ├── __init__.py
    ├── items.py
    ├── middlewares.py
    ├── pipelines.py
    ├── settings.py
    └── spiders
        ├── douban_spider.py
        └── __init__.py

2. Создайте модель данных сканера

Открытым./spider_douban/items.pyфайл, отредактируйте содержимое следующим образом:

import scrapy

class DoubanMovieItem(scrapy.Item):
    # 排名
    ranking = scrapy.Field()
    # 电影名称
    movie_name = scrapy.Field()
    # 评分
    score = scrapy.Field()
    # 评论人数
    score_num = scrapy.Field()

3. Создайте новый файл сканера

новый./spiders/douban_spider.pyфайл, отредактируйте содержимое следующим образом:

from scrapy import Request
from scrapy.spiders import Spider
from spider_douban.items import DoubanMovieItem

class DoubanMovieTop250Spider(Spider):
    name = 'douban_movie_top250'
    start_urls = {
        'https://movie.douban.com/top250'
        }
    '''
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36',
    }

    def start_requests(self):
        url = 'https://movie.douban.com/top250'
        yield Request(url, headers=self.headers)
    '''
    def parse(self, response):
        item = DoubanMovieItem()
        movies = response.xpath('//ol[@class="grid_view"]/li')
        print(movies)
        print('=============================================')
        for movie in movies:
            item['ranking'] = movie.xpath(
                './/div[@class="pic"]/em/text()').extract()[0]
            item['movie_name'] = movie.xpath(
                './/div[@class="hd"]/a/span[1]/text()').extract()[0]
            item['score'] = movie.xpath(
                './/div[@class="star"]/span[@class="rating_num"]/text()'
            ).extract()[0]
            item['score_num'] = movie.xpath(
                './/div[@class="star"]/span/text()').re(r'(\d+)人评价')[0]
            yield item
        
        next_url = response.xpath('//span[@class="next"]/a/@href').extract()

        if next_url:
            next_url = 'https://movie.douban.com/top250' + next_url[0]
            yield Request(next_url)

Функциональная запись каждой части файла сканера

douban_spider.pyФайл в основном состоит из нескольких частей.

модуль импорта

from scrapy import Request
from scrapy.spiders import Spider
from spider_douban.items import DoubanMovieItem

RequestКласс для запроса данных страницы для сканирования
SpiderКласс - это базовый класс рептилий
DoubanMovieItemмодель данных сканирования, которую мы установили на первом этапе

настройки по умолчанию

на основеspiderКласс рептилий определяется классомDoubanMovieTop250Spider, сначала определите основную информацию об искателе:

имя: имя краулера в проекте, который может быть запущен в каталоге проектаscrapy listПолучить список определенных сканеров
start_urls: это адрес первой просканированной страницы.
заголовки: это сообщение пользовательского агента, прикрепляемое при отправке запроса страницы на веб-сервер, сообщающее веб-серверу, какой тип браузера или устройства запрашивает страницу.Для веб-сайтов, которые не имеют простого механизма предотвращения сканирования, заголовки часть можно опустить.

Чтобы запутать веб-сервер, информация об агенте пользователя обычно определяется, когда сканер отправляет веб-запрос.Есть два способа записать ее здесь.

  • Первое определение заголовка:
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36',
    }

    def start_requests(self):
        url = 'https://movie.douban.com/top250'
        yield Request(url, headers=self.headers)

Видно, что при таком способе написанияstart_urlsОпределения нет, вместо него определеноstart_requestsфункция, начальный URL записывается в функцию. В то же время определяется словарь заголовков, и при отправке запроса Request словарь заголовков отправляется вместе. Этот метод написания прост и интуитивно понятен, недостаток в том, что во время выполнения проекта краулера все запросы являются атрибутом User-Agent.

  • Второе определение заголовка:
    start_urls = {
        'https://movie.douban.com/top250'
        }

Атрибут start_urls определяется просто и напрямую, а атрибут заголовка в Request определяется другими методами, которые будут рассмотрены позже.

обработчик синтаксического анализа

Разложение по предложениям 1. Создайте экземпляр элемента на основе определенного нами класса DoubanMovieItem.

item = DoubanMovieItem()

2. Разобрать страницу — получить фрейм контента

Анализируя исходный код страницы, мы видим, что информация о фильме на странице сохраняется в<ol>этикетка, это<ol>теги имеют уникальную таблицу стилейgrid_view, в то время как информация о каждом отдельном фильме хранится в<li>тег, следующий код получаетclassсобственностьgrid_viewиз<ol>все под маркой<li>Содержимое ярлыка.

movies = response.xpath('//ol[@class="grid_view"]/li')

3. Разобрать страницу - получить подпункты

в каждом<li>В метке также есть внутренняя структура, которая анализируется xpath(), и каждое содержимое анализируется и назначается каждому полю в экземпляре элемента. просмотревmovie.douban.com/top250Исходный код страницы позволяет легко найти, что определяет этот тег. Если мы посмотрим на переменный тип фильмов через функцию type(), то обнаружим, что его тип<class 'scrapy.selector.unified.SelectorList'>.<ol>каждый из тегов<li>Теги — это все элементы в этом списке, затем фильмы можно повторять.

Первый взгляд<li>Структура страницы в тегах:

Вы можете увидеть положение метки каждой части данных, которые нужно извлечь:

Рейтинг: Атрибут класса — pic<div>под этикеткой,<em>ярлык...
Название фильма: Атрибут класса hd<div>под этикеткой,<a>первый на этикетке<span>Этикетка...
Рейтинг: Атрибут класса - звезда<div>Под меткой атрибут класса — rating_num<span>ярлык...
Количество комментариев: те, чей атрибут класса имеет звездочку<div>под этикеткой,<span>в этикетке. Поскольку используется регулярное выражение re, не указано, какое из них<span>Этикетка.

Вернитесь к части кода, выполните итерацию по ранее определенным фильмам и получите данные, которые нужно захватить поэлементно.

        for movie in movies:
            item['ranking'] = movie.xpath(
                './/div[@class="pic"]/em/text()').extract()[0]
            item['movie_name'] = movie.xpath(
                './/div[@class="hd"]/a/span[1]/text()').extract()[0]
            item['score'] = movie.xpath(
                './/div[@class="star"]/span[@class="rating_num"]/text()'
            ).extract()[0]
            item['score_num'] = movie.xpath(
                './/div[@class="star"]/span/text()').re(r'(\d+)人评价')[0]
            yield item

4.Переход по URL (переворот страницы)

Если до сих пор, мы можемhttps://movie.douban.com/top250Просматривается первая страница страницы, но записей всего 25. Чтобы просканировать все 250 записей, необходимо выполнить следующий код:

        next_url = response.xpath('//span[@class="next"]/a/@href').extract()

        if next_url:
            next_url = 'https://movie.douban.com/top250' + next_url[0]
            yield Request(next_url)

Сначала проанализируйте страницу через xpath后页Ссылку и присвоить ее переменной next_url, если мы сейчас на первой странице, то парсим后页Ссылка?start=25&filter=. Соедините проанализированную ссылку на предыдущую страницу с полным URL-адресом, чтобы сформировать полный адрес, и снова выполните Request(), чтобы просканировать все 250 записей. Примечание. Результат, проанализированный xpath, представляет собой список, поэтому он записывается какnext_url[0].

4. Обработка случайных свойств головы (случайный пользовательский агент)

Реализовать отправку случайных атрибутов головы. В основном измените два файла:

settings.py

USER_AGENT_LIST = [
    'zspider/0.9-dev http://feedback.redkolibri.com/',
    'Xaldon_WebSpider/2.0.b1',
    'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) Speedy Spider (http://www.entireweb.com/about/search_tech/speedy_spider/)',
    'Mozilla/5.0 (compatible; Speedy Spider; http://www.entireweb.com/about/search_tech/speedy_spider/)',
    'Speedy Spider (Entireweb; Beta/1.3; http://www.entireweb.com/about/search_tech/speedyspider/)',
    'Speedy Spider (Entireweb; Beta/1.2; http://www.entireweb.com/about/search_tech/speedyspider/)',
    'Speedy Spider (Entireweb; Beta/1.1; http://www.entireweb.com/about/search_tech/speedyspider/)',
    'Speedy Spider (Entireweb; Beta/1.0; http://www.entireweb.com/about/search_tech/speedyspider/)',
    'Speedy Spider (Beta/1.0; www.entireweb.com)',
    'Speedy Spider (http://www.entireweb.com/about/search_tech/speedy_spider/)',
    'Speedy Spider (http://www.entireweb.com/about/search_tech/speedyspider/)',
    'Speedy Spider (http://www.entireweb.com)',
    'Sosospider+(+http://help.soso.com/webspider.htm)',
    'sogou spider',
    'Nusearch Spider (www.nusearch.com)',
    'nuSearch Spider (compatible; MSIE 4.01; Windows NT)',
    'lmspider (lmspider@scansoft.com)',
    'lmspider lmspider@scansoft.com',
    'ldspider (http://code.google.com/p/ldspider/wiki/Robots)',
    'iaskspider/2.0(+http://iask.com/help/help_index.html)',
    'iaskspider',
    'hl_ftien_spider_v1.1',
    'hl_ftien_spider',
    'FyberSpider (+http://www.fybersearch.com/fyberspider.php)',
    'FyberSpider',
    'everyfeed-spider/2.0 (http://www.everyfeed.com)',
    'envolk[ITS]spider/1.6 (+http://www.envolk.com/envolkspider.html)',
    'envolk[ITS]spider/1.6 ( http://www.envolk.com/envolkspider.html)',
    'Baiduspider+(+http://www.baidu.com/search/spider_jp.html)',
    'Baiduspider+(+http://www.baidu.com/search/spider.htm)',
    'BaiDuSpider',
    'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0) AddSugarSpiderBot www.idealobserver.com',
    ]

DOWNLOADER_MIDDLEWARES = {
    'spider_douban.middlewares.RandomUserAgentMiddleware': 400,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
}

USER_AGENT_LISTОпределены некоторые атрибуты пользовательского агента браузера. Их много в Интернете. Их можно найти и добавить напрямую. Следует отметить, что некоторая информация пользовательского агента поступает с мобильных устройств (мобильных телефонов или планшетов). Если не обращать внимания , данные могут быть запрошены. Существует большая разница с данными, которые вы видите;
DOWNLOADER_MIDDLEWARESОпределяет промежуточное ПО загрузчика, которое вызывается при отправке данных запроса страницы.

middlewares.py

from spider_douban.settings import USER_AGENT_LIST
import random

class RandomUserAgentMiddleware():
    def process_request(self, request, spider):
        ua  = random.choice(USER_AGENT_LIST)
        if ua:
            request.headers.setdefault('User-Agent', ua)

существуетRandomUserAgentMiddleware(), каждый раз, когда данные запроса отправляются, они будутUSER_AGENT_LISTСлучайным образом выбрать одинUser-Agentзаписывать.

5. Результаты сохраняются

редактироватьpipelines.pyдокумент:

from scrapy import signals
from scrapy.contrib.exporter import CsvItemExporter

class SpiderDoubanPipeline(CsvItemExporter):
    def __init__(self):
        self.files = {}

    @classmethod
    def from_crawler(cls, crawler):
        print('==========pipeline==========from_crawler==========')
        pipeline = cls()
        crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
        crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
        return pipeline

    def spider_opened(self, spider):
        savefile = open('douban_top250_export.csv', 'wb+')
        self.files[spider] = savefile
        print('==========pipeline==========spider_opened==========')
        self.exporter = CsvItemExporter(savefile)
        self.exporter.start_exporting()

    def spider_closed(self, spider):
        print('==========pipeline==========spider_closed==========')
        self.exporter.finish_exporting()
        savefile = self.files.pop(spider)
        savefile.close()

    def process_item(self, item, spider):
        print('==========pipeline==========process_item==========')
        print(type(item))
        self.exporter.export_item(item)
        return item

SpiderDoubanPipelineКласс создается сам по себе при создании проекта и модифицируется для сохранения файла.

def from_crawler(cls, crawler):

  • Если он существует, этот метод вызывается для создания экземпляра конвейера из Crawler. Он должен возвращать новый экземпляр конвейера. Объект захвата обеспечивает доступ ко всем основным компонентам Scrapy, таким как настройки и сигналы; это способ для конвейера получить к ним доступ и подключить их функциональность к Scrapy.

В этом методе определяется экземпляр сборщика данных (cls): «конвейер».

сигналы: Scrapy использует сигналы для уведомления о том, что что-то происходит. Вы можете поймать некоторые сигналы в своем проекте Scrapy (используя расширения), чтобы выполнить дополнительную работу или добавить дополнительную функциональность, расширяя Scrapy. Хотя сигнал предоставляет некоторые параметры, функция-обработчик не обязана получать все параметры — механизм диспетчеризации сигналов предоставляет только те параметры, которые принимает обработчик. Вы можете подключать (или отправлять свои) сигналы через Signals API.

connect: связать функцию приемника с сигналом. signal может быть любой объект, хотя Scrapy предоставляет некоторые предопределенные сигналы.

def spider_opened(self, spider):

  • Этот сигнал посылается, когда паук начинает ползать. Этот сигнал обычно используется для распределения ресурсов паука, но он также может делать что угодно. Этот сигнал поддерживает возврат deferred'ов.

В этом методе создается экземпляр файлового объекта:savefile.

CsvItemExporter(savefile): Формат выходного CSV-файла.Если вы добавите атрибут fields_to_export, он определит имена столбцов CSV по порядку.

def spider_closed(self, spider):

  • Этот сигнал отправляется, когда крестовина закрыта. Этот сигнал можно использовать для освобождения ресурсов, занятых каждым пауком, когда Spider_opened. Этот сигнал поддерживает возврат deferred'ов.

def process_item(self, item, spider):

  • Каждый компонент конвейера элементов должен вызывать этот метод. Этот метод должен возвращать объект Item (или любого унаследованного класса) или вызывать исключение DropItem. Отброшенный элемент не будет обрабатываться последующими компонентами конвейера.

включить конвейер

Чтобы конвейер, который мы определили, вступил в силу, в файле settings.py откройтеITEM_PIPELINESПримечания:

ITEM_PIPELINES = {
    'spider_douban.pipelines.SpiderDoubanPipeline': 300,
}

6. Запустите сканер

scrapy crawl douban_movie_top250

Запустите сканер, чтобы увидеть просканированные данные. . .

Если код конвейера не был написан ранее, вы также можете использовать следующую команду для прямого экспорта данных при выполнении сканера:

scrapy crawl douban_movie_top250 -o douban.csv

Увеличивать-oпараметр, вы можете сохранить просканированные данные вdouban.csvв файле. .

7. Проблема кодировки файлов

Я запускаю сканер на сервере Linux, и после создания файла csv он искажается при открытии с помощью Excel в системе win7. Я нашел несколько статей в Интернете, и некоторые статьи напрямую изменяют кодировку файлов Linux по умолчанию, но я чувствую, что это повлияет на другие проекты. Наконец, выберите относительно простой способ. Выполните следующие шаги для выполнения:

  1. Не открывайте CSV-файл напрямую с помощью Excel. Откройте Excel и создайте пустой лист.
  2. выберите数据вкладка, открыть获取外部数据середина自文本.
  3. существует导入文本文件В диалоговом окне выберите CSV-файл для импорта.
  4. существует文本导入向导 - 第1步в, набор文件原始格式за65001 : Unicode (UTF-8)
  5. Перейдите к следующему шагу и выберите через запятую, вы можете импортировать обычный текст.