Scrapy — TypeError: может объединять только str (не «список») в str

#python #python-3.x #web-scraping #scrapy #typeerror

#python #python-3.x #веб-очистка #scrapy #ошибка типа

Вопрос:

Пока я пытаюсь собрать список URL-адресов с веб-сайта и объединить их с базовым URL-адресом, затем продолжить его внутри страницы.

После объединения и обхода этих URL-адресов 1 на 1, затем сканируйте его детали. Слой похож MainPage на > Categories > List of Company > Details of each company (данные, которые я хочу)

это возвращает ошибку типа: может только объединять str (не «список») в str. Ниже приведен мой код для Scrapy Spider

 import scrapy
from scrapy.spiders import Rule
from scrapy.spiders import CrawlSpider
from scrapy.selector import Selector
# from urllib.parse import urljoin


class ZomatoSpider(scrapy.Spider):
    name = 'zomato'
    allowed_domain = ['foodbizmalaysia.com']
    start_urls = ['http://www.foodbizmalaysia.com/category/3/bakery-pastry-supplies?classid=DS-B42850']
    headers = {
        "accept": "text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "cookie": "dnetsid=5kegaefgfpb0efhf3idfxn30; afrvt=14846924c9bb4e87b5576addf94f8cc4; _ga=GA1.2.1937980614.1603360774; _gid=GA1.2.1358979332.1603360774"
    }


    def parse(self, response):
        url = "http://www.foodbizmalaysia.com/"

        yield scrapy.Request(url, 
            callback=self.parse_api, 
            headers=self.headers)
        


    def parse_api(self, response):
        base_url = 'http://www.foodbizmalaysia.com'
        sel = Selector(response)
        sites = sel.xpath('/html')
                
        for data in sites:
            categories = data.xpath('//div[@class="post_content"]/a[contains(@href, "category")]/@href').extract()
            category_url = base_url   categories

            request = scrapy.Request(
                category_url, 
                callback=self.parse_restaurant_company, 
                headers=self.headers
            ) 

            yield request


    def parse_restaurant_company(self, response):
        base_url = 'http://www.foodbizmalaysia.com'
        sel = Selector(response)
        sites = sel.xpath('/html')

        for data in sites:
            company = data.xpath('//a[contains(@id, "ContentPlaceHolder1_dgrdCompany_Hyperlink4_")]/@href').extract_first()
            company_url = base_url   company
            # for i in company:
            #     yield response.urljoin(
            #         'http://www.foodbizmalaysia.com', i[1:],
            #         callback=self.parse_company_details)

        request = scrapy.Request(
                company_url,
                callback=self.parse_company_details, 
                headers=self.headers

        )
        yield request

    def parse_company_details(self, response):
        sel = Selector(response)
        sites = sel.xpath('/html')

        yield {
            'name' : sites.xpath('//span[@class="coprofileh3"]/text()').get()
        }

  

Как показано ниже, это журнал после того, как я запустил scrapy runspider:

 2020-10-23 10:58:50 [scrapy.utils.log] INFO: Scrapy 2.4.0 started (bot: scrapybot)
2020-10-23 10:58:50 [scrapy.utils.log] INFO: Versions: lxml 4.5.0.0, libxml2 2.9.10, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 18.9.0, Python 3.8.6 (default, Sep 25 2020, 09:36:53) - [GCC 10.2.0], pyOpenSSL 19.1.0 (OpenSSL 1.1.1g  21 Apr 2020), cryptography 2.8, Platform Linux-5.5.0-kali2-amd64-x86_64-with-glibc2.29
2020-10-23 10:58:50 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.epollreactor.EPollReactor
2020-10-23 10:58:50 [scrapy.crawler] INFO: Overridden settings:
{'SPIDER_LOADER_WARN_ONLY': True}
2020-10-23 10:58:50 [scrapy.extensions.telnet] INFO: Telnet Password: 97316bde34a4b21d
2020-10-23 10:58:50 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.memusage.MemoryUsage',
 'scrapy.extensions.logstats.LogStats']
2020-10-23 10:58:50 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2020-10-23 10:58:50 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2020-10-23 10:58:50 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2020-10-23 10:58:50 [scrapy.core.engine] INFO: Spider opened
2020-10-23 10:58:50 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2020-10-23 10:58:50 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6024
2020-10-23 10:58:52 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.foodbizmalaysia.com/category/3/bakery-pastry-supplies?classid=DS-B42850> (referer: None)
2020-10-23 10:58:54 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.foodbizmalaysia.com/> (referer: http://www.foodbizmalaysia.com/category/3/bakery-pastry-supplies?classid=DS-B42850)
2020-10-23 10:58:54 [scrapy.core.scraper] ERROR: Spider error processing <GET http://www.foodbizmalaysia.com/> (referer: http://www.foodbizmalaysia.com/category/3/bakery-pastry-supplies?classid=DS-B42850)
Traceback (most recent call last):
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/utils/defer.py", line 120, in iter_errback
    yield next(it)
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/utils/python.py", line 353, in __next__
    return next(self.data)
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/utils/python.py", line 353, in __next__
    return next(self.data)
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/core/spidermw.py", line 62, in _evaluate_iterable
    for r in iterable:
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/spidermiddlewares/offsite.py", line 29, in process_spider_output
    for x in result:
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/core/spidermw.py", line 62, in _evaluate_iterable
    for r in iterable:
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/spidermiddlewares/referer.py", line 340, in <genexpr>
    return (_set_referer(r) for r in result or ())
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/core/spidermw.py", line 62, in _evaluate_iterable
    for r in iterable:
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/spidermiddlewares/urllength.py", line 37, in <genexpr>
    return (r for r in result or () if _filter(r))
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/core/spidermw.py", line 62, in _evaluate_iterable
    for r in iterable:
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/spidermiddlewares/depth.py", line 58, in <genexpr>
    return (r for r in result or () if _filter(r))
  File "/home/limjack4511/.local/lib/python3.8/site-packages/scrapy/core/spidermw.py", line 62, in _evaluate_iterable
    for r in iterable:
  File "/home/limjack4511/Dev/0temp/zomato.py", line 34, in parse_api
    category_url = base_url   categories
TypeError: can only concatenate str (not "list") to str
2020-10-23 10:58:54 [scrapy.core.engine] INFO: Closing spider (finished)
2020-10-23 10:58:54 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 752,
 'downloader/request_count': 2,
 'downloader/request_method_count/GET': 2,
 'downloader/response_bytes': 34411,
 'downloader/response_count': 2,
 'downloader/response_status_count/200': 2,
 'elapsed_time_seconds': 3.888395,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2020, 10, 23, 2, 58, 54, 321201),
 'log_count/DEBUG': 2,
 'log_count/ERROR': 1,
 'log_count/INFO': 10,
 'memusage/max': 53633024,
 'memusage/startup': 53633024,
 'request_depth_max': 1,
 'response_received_count': 2,
 'scheduler/dequeued': 2,
 'scheduler/dequeued/memory': 2,
 'scheduler/enqueued': 2,
 'scheduler/enqueued/memory': 2,
 'spider_exceptions/TypeError': 1,
 'start_time': datetime.datetime(2020, 10, 23, 2, 58, 50, 432806)}
2020-10-23 10:58:54 [scrapy.core.engine] INFO: Spider closed (finished)
  

Комментарии:

1. Не могли бы вы включить полную обратную трассировку?

2. также похоже yield response.urljoin('http://www.foodbizmalaysia.com', i[1:],callback=self.parse_company_details) , что это может вызвать у вас проблему; в частности i[1:]

3. Привет @Ironkey теперь я пытаюсь удалить i [1:] и, как показано ниже:

4. @J9cki11er Пожалуйста, отредактируйте свой вопрос и добавьте дополнительную информацию.

5. @renatodvc обновили

Ответ №1:

В вашем коде есть некоторые проблемы, из-за которых кажется, что выполняемый вами код ОТЛИЧАЕТСЯ от опубликованного вами. Например, это ваш parse_api метод (скопированный и вставленный):

 def parse_api(self, response):
    base_url = 'http://www.foodbizmalaysia.com'
    sel = Selector(response)
    sites = sel.xpath('/html')
            
    for data in sites:
        categories = data.xpath('//div[@class="post_content"]/a[contains(@href, "category")]/@href').extract()


        request = scrapy.Request(
            category_url, 
            callback=self.parse_restaurant_company, 
            headers=self.headers
        ) 
        yield request
  

Это привело бы к возникновению NameError as category_url , которое нигде не определено. Это не единственное несоответствие, вот фрагмент вашего журнала выполнения:

   File "/home/limjack4511/Dev/0temp/zomato.py", line 33, in parse_api
    category_url = base_url   categories
TypeError: can only concatenate str (not "list") to str
  

Это говорит мне, что в методе parse_api эта строка возвращает ошибку: category_url = base_url categories , но эта строка не существует в этом методе (по крайней мере, не в том, который вы опубликовали), у вас есть та же строка, но внутри другого метода, вызывается parse_restaurant_company .


Ошибка сообщает вам, что вы пытаетесь объединить строку со списком, что означает, что from base_url и categories one — это строка, а другой — список. Я не могу сказать, что есть что, потому что я не могу доверять опубликованному вами коду.


Редактировать:

Теперь, имея полный код, я могу рассказать вам, в чем проблема: ( parse_api метод)

     for data in sites:
        categories = data.xpath('//div[@class="post_content"]/a[contains(@href, "category")]/@href').extract()
        category_url = base_url   categories
  

Вы вызываете .extract() при определении категорий. Метод extract возвращает список, а не строку. Замените его на .get() или .extract_first()

С другой стороны: вы, вероятно, захотите использовать data.xpath('.//div[... вместо data.xpath('//div[... , потому что в первом случае этот XPath будет искать внутри data узла. Без . этого он будет искать XPath во всем документе, игнорируя контекст, уже установленный data переменной.

Комментарии:

1. Я перепечатал весь код, и журнал выполнения на мой вопрос может посмотреть.

2. @J9cki11er только что отредактировал мой ответ, дайте мне знать, если это помогло.

3. Привет @renatodvc, я попробовал ваш метод, который с помощью extract_first() позволяет сканировать данные. Но в настоящее время он сканирует только 1 данные с домашней страницы вместо того, чтобы проходить по всей ссылке href. есть идеи, как паук может сканировать и проходить через все href, которые я сканировал?

4. @J9cki11er Я не совсем понимаю, что вы пытаетесь там сделать. Вы повторяете итерацию sites , но эта переменная вернет только ОДИН селектор (по мере выбора //html ), поэтому он вообще не повторяется. Может быть, вам следует повторить то, что вы определяете как categories ? В этом случае вам нужно определить его вне цикла и использовать .extract() снова (так как вам понадобится список).