2016-04-05 2 views
2

Я пытаюсь записать название курса с количеством студентов от Udacity, чтобы узнать, какие курсы являются самыми популярными. Мне удается создать код для элемента:Скребковый интерактивный сайт

import scrapy 
class UdacityItem(scrapy.Item): 
    name=scrapy.Field() 
    users=scrapy.Field() 

и паук:

import scrapy 
from Udacity.items import UdacityItem 
import re 

class DmozSpider(scrapy.Spider): 
    name = "UdSpider" 
    allowed_domains = ["udacity.com"] 
    start_urls = ["https://www.udacity.com/courses/all"] 

    def parse(self, response): 

     sites = response.xpath('//h3/a') 
     for s in sites: 
      t=UdacityItem() 
      #name & url 
      t['name']=s.xpath('text()').extract()[0].strip() 
      url=response.urljoin(s.xpath('@href').extract()[0]) 
      #request 
      req=scrapy.Request(url, callback=self.second) 
      req.meta['item']=t 
      #execute 
      yield req 

    def second(self,response): 
     t=response.meta['item'] 
     strong =response.xpath('//strong[@data-course-student-count]/text()').extract()[0] 
     t['users']=strong 
     yield t 

В результате я получаю название курса, но вместо того, чтобы число студентов я получаю текст «тысячи ». Когда я открываю example website в браузере, я вижу, что «тысячи» - это базовое значение, а позже (через 1-2 секунды) этот текст меняется на правильный номер (который я хочу получить).

А вот мои вопросы:

  1. Почему эта замена происходит? Это код JavaScript? Я бы хотел бы понять механизм этого изменения.
  2. Как я могу захватить надлежащее количество студентов, использующих scrapy? Надеюсь, это возможно.

Заранее благодарю вас за помощь.

ответ

2

Чтобы рассчитывать на учащихся, вы должны имитировать запрос API для https://www.udacity.com/api/summaries конечной точки для конкретного курса идентификатор, который можно извлечь из самого URL - например, это ud898 для https://www.udacity.com/course/javascript-promises--ud898 URL.

Полный паук:

import json 

import re 
from urllib import quote_plus 

import scrapy 


class UdacityItem(scrapy.Item): 
    name = scrapy.Field() 
    users = scrapy.Field() 


class DmozSpider(scrapy.Spider): 
    name = "UdSpider" 
    allowed_domains = ["udacity.com"] 
    start_urls = ["https://www.udacity.com/courses/all"] 

    def parse(self, response): 
     sites = response.xpath('//h3/a') 
     for s in sites: 
      t = UdacityItem() 
      # name & url 
      t['name'] = s.xpath('text()').extract()[0].strip() 
      url = response.urljoin(s.xpath('@href').extract()[0]) 
      # request 
      req = scrapy.Request(url, callback=self.second) 
      req.meta['item'] = t 
      # execute 
      yield req 

    def second(self, response): 
     queries = [{ 
      "limit": 1, 
      "model": "CourseStudentsSummary", 
      "locator": { 
       "sample_frequency": "daily", 
       "content_context": [{ 
        "node_key": re.search(r'--(.*?)$', response.url).group(1) 
       }] 
      } 
     }] 
     yield scrapy.Request(method="GET", 
          url="https://www.udacity.com/api/summaries?queries=" + quote_plus(json.dumps(queries)), 
          callback=self.parse_totals) 

    def parse_totals(self, response): 
     print(json.loads(response.body[5:].strip())["summaries"]["default"][0]["data"]["total_enrollments"]) 
+0

Уважаемый alecxe. Спасибо за ваш ответ. Теперь у меня есть решение, но все же я не вижу связи между загрузкой html и отправкой запроса API. Когда я смотрю на документ, я не могу найти код, ответственный за это. Как вы выясните, что вам нужно сделать запрос API? Я предполагаю, что код JavaScript отвечает за то, что загружено в конце документа, верно? – michalk

+1

@michalk Я использовал инструменты для разработчиков браузера - вкладка «Сеть» и только отфильтрованные запросы XHR; есть несколько запросов XHR, сделанных во время загрузки страницы, я их осмотрел и нашел общий охват ответа. Затем, работая над тем, чтобы сделать тот же запрос в Scrapy..hope, который помогает. – alecxe

Смежные вопросы