#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()
снова (так как вам понадобится список).