2016-01-23 2 views
1

This - это сайт, над которым я работаю. На каждой странице есть 18 сообщений в таблице. Я хочу получить доступ к каждому сообщению и очистить его содержимое и повторить его для первых 5 страниц.Следуйте каждой ссылке страницы и очищайте содержимое, Scrapy + Selenium

Мой подход заключается в том, чтобы заставить моего паука очистить все ссылки на 5 страницах и перебрать их, чтобы получить контент. Поскольку кнопка «следующая страница» и определенный текст в каждом сообщении написаны JavaScript, я использую Selenium и Scrapy. Я запустил свой паук и увидел, что Firefox webdriver отображает первые 5 страниц, но затем паук остановился, не соскабливая какой-либо контент. Scrapy также не возвращает сообщение об ошибке.

Теперь я подозреваю, что отказ может быть из-за:

1) Никакой связи не хранится в all_links.

2) Каким-то образом parse_content не запускался.

Мой диагноз может быть неправильным, и мне нужна помощь в поиске проблемы. Большое спасибо!

Это мой паук:

import scrapy 
from bjdaxing.items_bjdaxing import BjdaxingItem 
from selenium import webdriver 
from scrapy.http import TextResponse 
import time 

all_links = [] # a global variable to store post links 


class Bjdaxing(scrapy.Spider): 
    name = "daxing" 

    allowed_domains = ["bjdx.gov.cn"] # DO NOT use www in allowed domains 
    start_urls = ["http://app.bjdx.gov.cn/cms/daxing/lookliuyan_bjdx.jsp"] # This has to start with http 

    def __init__(self): 
     self.driver = webdriver.Firefox() 

    def parse(self, response): 
     self.driver.get(response.url) # request the start url in the browser   

     i = 1 

     while i <= 5: # The number of pages to be scraped in this session 

      response = TextResponse(url = response.url, body = self.driver.page_source, encoding='utf-8') # Assign page source to response. I can treat response as if it's a normal scrapy project.   

      global all_links 
      all_links.extend(response.xpath("//a/@href").extract()[0:18]) 

      next = self.driver.find_element_by_xpath(u'//a[text()="\u4e0b\u9875\xa0"]') # locate "next" button 
      next.click() # Click next page    
      time.sleep(2) # Wait a few seconds for next page to load. 

      i += 1 


    def parse_content(self, response): 
     item = BjdaxingItem() 
     global all_links 
     for link in all_links: 
      self.driver.get("http://app.bjdx.gov.cn/cms/daxing/") + link 

      response = TextResponse(url = response.url, body = self.driver.page_source, encoding = 'utf-8') 

      if len(response.xpath("//table/tbody/tr[1]/td[2]/text()").extract() > 0): 
       item['title'] =  response.xpath("//table/tbody/tr[1]/td[2]/text()").extract() 
      else: 
       item['title'] = ""  

      if len(response.xpath("//table/tbody/tr[3]/td[2]/text()").extract() > 0): 
       item['netizen'] = response.xpath("//table/tbody/tr[3]/td[2]/text()").extract() 
      else: 
       item['netizen'] = ""  

      if len(response.xpath("//table/tbody/tr[3]/td[4]/text()").extract() > 0): 
       item['sex'] = response.xpath("//table/tbody/tr[3]/td[4]/text()").extract() 
      else: 
       item['sex'] = "" 

      if len(response.xpath("//table/tbody/tr[5]/td[2]/text()").extract() > 0): 
       item['time1'] = response.xpath("//table/tbody/tr[5]/td[2]/text()").extract() 
      else: 
       item['time1'] = "" 

      if len(response.xpath("//table/tbody/tr[11]/td[2]/text()").extract() > 0): 
       item['time2'] = response.xpath("//table/tbody/tr[11]/td[2]/text()").extract() 
      else: 
       item['time2'] = "" 

      if len(response.xpath("//table/tbody/tr[7]/td[2]/text()").extract()) > 0: 
       question = "".join(response.xpath("//table/tbody/tr[7]/td[2]/text()").extract()) 
       item['question'] = "".join(map(unicode.strip, question)) 
      else: item['question'] = "" 

      if len(response.xpath("//table/tbody/tr[9]/td[2]/text()").extract()) > 0: 
       reply = "".join(response.xpath("//table/tbody/tr[9]/td[2]/text()").extract()) 
       item['reply'] = "".join(map(unicode.strip, reply)) 
      else: item['reply'] = ""  

      if len(response.xpath("//table/tbody/tr[13]/td[2]/text()").extract()) > 0: 
       agency = "".join(response.xpath("//table/tbody/tr[13]/td[2]/text()").extract()) 
       item['agency'] = "".join(map(unicode.strip, agency)) 
      else: item['agency'] = ""  

      yield item 

ответ

1

Множественные проблемы и возможные улучшения здесь:

  • у вас нет какой-либо "связи" между parse() и parse_content() методами
  • использованием global переменные, как правило, представляют собой плохую практику.
  • не нужен selenium здесь вообще. Для того, чтобы следовать пагинацию вам просто нужно сделать запрос POST на тот же URL обеспечивая параметр currPage

Идея заключается в том, чтобы использовать .start_requests() и создать список/очереди запросов для обработки нумерации. Следуйте за разбиением на страницы и соберите ссылки со стола. Когда очередь запросов пуста, переключитесь на следующие ранее собранные ссылки. Реализация:

import json 
from urlparse import urljoin 

import scrapy 


NUM_PAGES = 5 

class Bjdaxing(scrapy.Spider): 
    name = "daxing" 

    allowed_domains = ["bjdx.gov.cn"] # DO NOT use www in allowed domains 

    def __init__(self): 
     self.pages = [] 
     self.links = [] 

    def start_requests(self): 
     self.pages = [scrapy.Request("http://app.bjdx.gov.cn/cms/daxing/lookliuyan_bjdx.jsp", 
            body=json.dumps({"currPage": str(page)}), 
            method="POST", 
            callback=self.parse_page, 
            dont_filter=True) 
         for page in range(1, NUM_PAGES + 1)] 

     yield self.pages.pop() 

    def parse_page(self, response): 
     base_url = response.url 
     self.links += [urljoin(base_url, link) for link in response.css("table tr td a::attr(href)").extract()] 

     try: 
      yield self.pages.pop() 
     except IndexError: # no more pages to follow, going over the gathered links 
      for link in self.links: 
       yield scrapy.Request(link, callback=self.parse_content) 

    def parse_content(self, response): 
     # your parse_content method here 
+0

большое спасибо за решение. Я тестировал его, и он работал хорошо. Тем не менее, я все еще пытаюсь понять, как 'yield self.pages.pop()' внутри 'parge_page()' срабатывает 'except'. Я понимаю, что каждый раз 'start_requests()' помещает страницу в 'self.pages', а' self.pages.pop() 'в' parse_page() 'вынимает ее. Поэтому в пятый раз 'self.pages' не должен быть пустым, но в нем есть одна страница, и' yield self.pages.pop() 'должен его уступить. Но, видимо, мое понимание неверно. Не могли бы вы помочь мне узнать, где я был неправ? –

+1

@JosephZhou уверен, 'self.pages' имеет внутреннюю очередь запросов внутри. Каждый раз, когда вызывается 'pop()', в очереди еще один запрос меньше. Когда очередь пуста и запросов нет внутри, 'pop()' будет генерировать «IndexError' - pop из пустой ошибки списка, которая для нас является признаком того, что теперь мы можем начинать следовать ссылкам. Надеюсь, это все ясно. Благодарю. – alecxe

+0

спасибо за ваш быстрый ответ. Но, я думаю, я все еще что-то пропустил. Не существует ли всего пять запросов в 'self.pages' и пять вызовов' pop() '? Как это может привести к ошибке списка? Только шестой 'pop()' вызовет пустую очередь, не так ли? –

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