From 815d6160cfac5c0b764d01fc9d4ebc4ab7793aac Mon Sep 17 00:00:00 2001 From: Jakob de Maeyer Date: Mon, 16 Nov 2015 18:28:49 +0100 Subject: [PATCH 01/79] Add from_crawler constructor for feed exporters and storages --- scrapy/extensions/feedexport.py | 12 +++++-- scrapy/middleware.py | 9 ++--- scrapy/utils/misc.py | 24 +++++++++++++ tests/test_feedexport.py | 30 ++++++++++++++++ tests/test_utils_misc/__init__.py | 58 ++++++++++++++++++++++++++++++- 5 files changed, 122 insertions(+), 11 deletions(-) diff --git a/scrapy/extensions/feedexport.py b/scrapy/extensions/feedexport.py index 5f133fbde..70c302fba 100644 --- a/scrapy/extensions/feedexport.py +++ b/scrapy/extensions/feedexport.py @@ -21,7 +21,7 @@ from w3lib.url import file_uri_to_path from scrapy import signals from scrapy.utils.ftp import ftp_makedirs_cwd from scrapy.exceptions import NotConfigured -from scrapy.utils.misc import load_object +from scrapy.utils.misc import create_instance, load_object from scrapy.utils.log import failure_to_exc_info from scrapy.utils.python import without_none_values from scrapy.utils.boto import is_botocore @@ -181,6 +181,7 @@ class FeedExporter(object): @classmethod def from_crawler(cls, crawler): o = cls(crawler.settings) + o.crawler = crawler crawler.signals.connect(o.open_spider, signals.spider_opened) crawler.signals.connect(o.close_spider, signals.spider_closed) crawler.signals.connect(o.item_scraped, signals.item_scraped) @@ -253,11 +254,16 @@ class FeedExporter(object): logger.error("Unknown feed storage scheme: %(scheme)s", {'scheme': scheme}) + def _get_instance(self, objcls, *args, **kwargs): + return create_instance( + objcls, self.settings, getattr(self, 'crawler', None), + *args, **kwargs) + def _get_exporter(self, *args, **kwargs): - return self.exporters[self.format](*args, **kwargs) + return self._get_instance(self.exporters[self.format], *args, **kwargs) def _get_storage(self, uri): - return self.storages[urlparse(uri).scheme](uri) + return self._get_instance(self.storages[urlparse(uri).scheme], uri) def _get_uri_params(self, spider): params = {} diff --git a/scrapy/middleware.py b/scrapy/middleware.py index be36f977e..f2240984c 100644 --- a/scrapy/middleware.py +++ b/scrapy/middleware.py @@ -3,7 +3,7 @@ import logging import pprint from scrapy.exceptions import NotConfigured -from scrapy.utils.misc import load_object +from scrapy.utils.misc import create_instance, load_object from scrapy.utils.defer import process_parallel, process_chain, process_chain_both logger = logging.getLogger(__name__) @@ -32,12 +32,7 @@ class MiddlewareManager(object): for clspath in mwlist: try: mwcls = load_object(clspath) - if crawler and hasattr(mwcls, 'from_crawler'): - mw = mwcls.from_crawler(crawler) - elif hasattr(mwcls, 'from_settings'): - mw = mwcls.from_settings(settings) - else: - mw = mwcls() + mw = create_instance(mwcls, settings, crawler) middlewares.append(mw) enabled.append(clspath) except NotConfigured as e: diff --git a/scrapy/utils/misc.py b/scrapy/utils/misc.py index 35f855007..8eb1aabb5 100644 --- a/scrapy/utils/misc.py +++ b/scrapy/utils/misc.py @@ -117,3 +117,27 @@ def md5sum(file): def rel_has_nofollow(rel): """Return True if link rel attribute has nofollow type""" return True if rel is not None and 'nofollow' in rel.split() else False + +def create_instance(objcls, settings, crawler, *args, **kwargs): + """Construct a class instance using its ``from_crawler`` or + ``from_settings`` constructors, if available. + + At least one of ``settings`` and ``crawler`` needs to be different from + ``None``. If ``settings `` is ``None``, ``crawler.settings`` will be used. + If ``crawler`` is ``None``, only the ``from_settings`` constructor will be + tried. + + ``*args`` and ``**kwargs`` are forwarded to the constructors. + + Raises ``ValueError`` if both ``settings`` and ``crawler`` are ``None``. + """ + if settings is None: + if crawler is None: + raise ValueError("Specifiy at least one of settings and crawler.") + settings = crawler.settings + if crawler and hasattr(objcls, 'from_crawler'): + return objcls.from_crawler(crawler, *args, **kwargs) + elif hasattr(objcls, 'from_settings'): + return objcls.from_settings(settings, *args, **kwargs) + else: + return objcls(*args, **kwargs) diff --git a/tests/test_feedexport.py b/tests/test_feedexport.py index f55927121..08f7e4d8d 100644 --- a/tests/test_feedexport.py +++ b/tests/test_feedexport.py @@ -16,6 +16,7 @@ from tests.mockserver import MockServer from w3lib.url import path_to_file_uri import scrapy +from scrapy.exporters import CsvItemExporter from scrapy.extensions.feedexport import ( IFeedStorage, FileFeedStorage, FTPFeedStorage, S3FeedStorage, StdoutFeedStorage, @@ -159,6 +160,23 @@ class StdoutFeedStorageTest(unittest.TestCase): self.assertEqual(out.getvalue(), b"content") +class FromCrawlerMixin(object): + init_with_crawler = False + + @classmethod + def from_crawler(cls, crawler, *args, **kwargs): + cls.init_with_crawler = True + return cls(*args, **kwargs) + + +class FromCrawlerCsvItemExporter(CsvItemExporter, FromCrawlerMixin): + pass + + +class FromCrawlerFileFeedStorage(FileFeedStorage, FromCrawlerMixin): + pass + + class FeedExportTest(unittest.TestCase): class MyItem(scrapy.Item): @@ -599,3 +617,15 @@ class FeedExportTest(unittest.TestCase): data = yield self.exported_data(items, settings) print(row['format'], row['indent']) self.assertEqual(row['expected'], data) + + @defer.inlineCallbacks + def test_init_exporters_storages_with_crawler(self): + settings = { + 'FEED_EXPORTERS': {'csv': 'tests.test_feedexport.' + 'FromCrawlerCsvItemExporter'}, + 'FEED_STORAGES': {'file': 'tests.test_feedexport.' + 'FromCrawlerFileFeedStorage'}, + } + yield self.exported_data({}, settings) + self.assertTrue(FromCrawlerCsvItemExporter.init_with_crawler) + self.assertTrue(FromCrawlerFileFeedStorage.init_with_crawler) diff --git a/tests/test_utils_misc/__init__.py b/tests/test_utils_misc/__init__.py index 01460a10b..b95bba5c1 100644 --- a/tests/test_utils_misc/__init__.py +++ b/tests/test_utils_misc/__init__.py @@ -3,7 +3,9 @@ import os import unittest from scrapy.item import Item, Field -from scrapy.utils.misc import load_object, arg_to_iter, walk_modules +from scrapy.utils.misc import arg_to_iter, create_instance, load_object, walk_modules + +from tests import mock __doctests__ = ['scrapy.utils.misc'] @@ -74,5 +76,59 @@ class UtilsMiscTestCase(unittest.TestCase): self.assertEqual(list(arg_to_iter({'a':1})), [{'a': 1}]) self.assertEqual(list(arg_to_iter(TestItem(name="john"))), [TestItem(name="john")]) + def test_create_instance(self): + settings = mock.MagicMock() + crawler = mock.MagicMock(spec_set=['settings']) + args = (True, 100.) + kwargs = {'key': 'val'} + + def _test_with_settings(mock, settings): + create_instance(mock, settings, None, *args, **kwargs) + if hasattr(mock, 'from_crawler'): + self.assertEqual(mock.from_crawler.call_count, 0) + if hasattr(mock, 'from_settings'): + mock.from_settings.assert_called_once_with(settings, *args, + **kwargs) + self.assertEqual(mock.call_count, 0) + else: + mock.assert_called_once_with(*args, **kwargs) + + def _test_with_crawler(mock, settings, crawler): + create_instance(mock, settings, crawler, *args, **kwargs) + if hasattr(mock, 'from_crawler'): + mock.from_crawler.assert_called_once_with(crawler, *args, + **kwargs) + if hasattr(mock, 'from_settings'): + self.assertEqual(mock.from_settings.call_count, 0) + self.assertEqual(mock.call_count, 0) + elif hasattr(mock, 'from_settings'): + mock.from_settings.assert_called_once_with(settings, *args, + **kwargs) + self.assertEqual(mock.call_count, 0) + else: + mock.assert_called_once_with(*args, **kwargs) + + # Check usage of correct constructor using four mocks: + # 1. with no alternative constructors + # 2. with from_settings() constructor + # 3. with from_crawler() constructor + # 4. with from_settings() and from_crawler() constructor + spec_sets = ([], ['from_settings'], ['from_crawler'], + ['from_settings', 'from_crawler']) + for specs in spec_sets: + m = mock.MagicMock(spec_set=specs) + _test_with_settings(m, settings) + m.reset_mock() + _test_with_crawler(m, settings, crawler) + + # Check adoption of crawler settings + m = mock.MagicMock(spec_set=['from_settings']) + create_instance(m, None, crawler, *args, **kwargs) + m.from_settings.assert_called_once_with(crawler.settings, *args, + **kwargs) + + with self.assertRaises(ValueError): + create_instance(m, None, None) + if __name__ == "__main__": unittest.main() From 4d77c3084e64d351e3acf9092b4171d4418925fe Mon Sep 17 00:00:00 2001 From: Jakob de Maeyer Date: Mon, 23 Nov 2015 12:24:05 +0100 Subject: [PATCH 02/79] Add from_crawler constructor to S3FeedStorage --- scrapy/extensions/feedexport.py | 27 ++++++++++++++++++++--- tests/test_feedexport.py | 38 ++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/scrapy/extensions/feedexport.py b/scrapy/extensions/feedexport.py index 70c302fba..067887d94 100644 --- a/scrapy/extensions/feedexport.py +++ b/scrapy/extensions/feedexport.py @@ -93,12 +93,28 @@ class FileFeedStorage(object): class S3FeedStorage(BlockingFeedStorage): - def __init__(self, uri): + def __init__(self, uri, access_key=None, secret_key=None): + # BEGIN Backwards compatibility for initialising without keys (and + # without using from_crawler) from scrapy.conf import settings + no_defaults = access_key is None and secret_key is None + if no_defaults and ('AWS_ACCESS_KEY_ID' in settings or + 'AWS_SECRET_ACCESS_KEY' in settings): + import warnings + from scrapy.exceptions import ScrapyDeprecationWarning + warnings.warn( + "Initialising `scrapy.extensions.feedexport.S3FeedStorage` " + "without AWS keys is deprecated. Please supply credentials or " + "use the `from_crawler()` constructor.", + category=ScrapyDeprecationWarning, + stacklevel=2 + ) + access_key = settings['AWS_ACCESS_KEY_ID'] + secret_key = settings['AWS_SECRET_ACCESS_KEY'] u = urlparse(uri) self.bucketname = u.hostname - self.access_key = u.username or settings['AWS_ACCESS_KEY_ID'] - self.secret_key = u.password or settings['AWS_SECRET_ACCESS_KEY'] + self.access_key = u.username or access_key + self.secret_key = u.password or secret_key self.is_botocore = is_botocore() self.keyname = u.path[1:] # remove first "/" if self.is_botocore: @@ -111,6 +127,11 @@ class S3FeedStorage(BlockingFeedStorage): import boto self.connect_s3 = boto.connect_s3 + @classmethod + def from_crawler(cls, crawler, uri): + return cls(uri, crawler.settings['AWS_ACCESS_KEY_ID'], + crawler.settings['AWS_SECRET_ACCESS_KEY']) + def _store_in_thread(self, file): file.seek(0) if self.is_botocore: diff --git a/tests/test_feedexport.py b/tests/test_feedexport.py index 08f7e4d8d..eeb1bc2a4 100644 --- a/tests/test_feedexport.py +++ b/tests/test_feedexport.py @@ -6,12 +6,14 @@ from io import BytesIO import tempfile import shutil from six.moves.urllib.parse import urlparse +import warnings from zope.interface.verify import verifyObject from twisted.trial import unittest from twisted.internet import defer from scrapy.crawler import CrawlerRunner from scrapy.settings import Settings +from tests import mock from tests.mockserver import MockServer from w3lib.url import path_to_file_uri @@ -131,13 +133,47 @@ class BlockingFeedStorageTest(unittest.TestCase): class S3FeedStorageTest(unittest.TestCase): + @mock.patch('scrapy.conf.settings', new={'AWS_ACCESS_KEY_ID': 'conf_key', + 'AWS_SECRET_ACCESS_KEY': 'conf_secret'}, create=True) + def test_parse_credentials(self): + try: + import boto + except ImportError: + raise unittest.SkipTest("S3FeedStorage requires boto") + aws_credentials = {'AWS_ACCESS_KEY_ID': 'settings_key', + 'AWS_SECRET_ACCESS_KEY': 'settings_secret'} + crawler = get_crawler(settings_dict=aws_credentials) + # Instantiate with crawler + storage = S3FeedStorage.from_crawler(crawler, + 's3://mybucket/export.csv') + self.assertEqual(storage.access_key, 'settings_key') + self.assertEqual(storage.secret_key, 'settings_secret') + # Instantiate directly + storage = S3FeedStorage('s3://mybucket/export.csv', + aws_credentials['AWS_ACCESS_KEY_ID'], + aws_credentials['AWS_SECRET_ACCESS_KEY']) + self.assertEqual(storage.access_key, 'settings_key') + self.assertEqual(storage.secret_key, 'settings_secret') + # URI priority > settings priority + storage = S3FeedStorage('s3://uri_key:uri_secret@mybucket/export.csv', + aws_credentials['AWS_ACCESS_KEY_ID'], + aws_credentials['AWS_SECRET_ACCESS_KEY']) + self.assertEqual(storage.access_key, 'uri_key') + self.assertEqual(storage.secret_key, 'uri_secret') + # Backwards compatibility for initialising without settings + with warnings.catch_warnings(record=True) as w: + storage = S3FeedStorage('s3://mybucket/export.csv') + self.assertEqual(storage.access_key, 'conf_key') + self.assertEqual(storage.secret_key, 'conf_secret') + self.assertTrue('without AWS keys' in str(w[-1].message)) + @defer.inlineCallbacks def test_store(self): assert_aws_environ() uri = os.environ.get('S3_TEST_FILE_URI') if not uri: raise unittest.SkipTest("No S3 URI available for testing") - storage = S3FeedStorage(uri) + storage = S3FeedStorage(uri, Settings()) verifyObject(IFeedStorage, storage) file = storage.open(scrapy.Spider("default")) expected_content = b"content: \xe2\x98\x83" From 6a38fc39f8fd1344bc41fcf50fe0e0af27ec74c4 Mon Sep 17 00:00:00 2001 From: Eugenio Lacuesta Date: Thu, 19 Jul 2018 11:56:23 -0300 Subject: [PATCH 03/79] Include flags when copying requests --- scrapy/http/request/__init__.py | 2 +- tests/test_http_request.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/scrapy/http/request/__init__.py b/scrapy/http/request/__init__.py index 13a92ffa0..cd4360483 100644 --- a/scrapy/http/request/__init__.py +++ b/scrapy/http/request/__init__.py @@ -91,7 +91,7 @@ class Request(object_ref): """Create a new Request with the same attributes except for those given new values. """ - for x in ['url', 'method', 'headers', 'body', 'cookies', 'meta', + for x in ['url', 'method', 'headers', 'body', 'cookies', 'meta', 'flags', 'encoding', 'priority', 'dont_filter', 'callback', 'errback']: kwargs.setdefault(x, getattr(self, x)) cls = kwargs.pop('cls', self.__class__) diff --git a/tests/test_http_request.py b/tests/test_http_request.py index a042f03b6..fc89229c6 100644 --- a/tests/test_http_request.py +++ b/tests/test_http_request.py @@ -174,7 +174,8 @@ class RequestTest(unittest.TestCase): def somecallback(): pass - r1 = self.request_class("http://www.example.com", callback=somecallback, errback=somecallback) + r1 = self.request_class("http://www.example.com", flags=['f1', 'f2'], + callback=somecallback, errback=somecallback) r1.meta['foo'] = 'bar' r2 = r1.copy() @@ -184,6 +185,10 @@ class RequestTest(unittest.TestCase): assert r2.callback is r1.callback assert r2.errback is r2.errback + # make sure flags list is shallow copied + assert r1.flags is not r2.flags, "flags must be a shallow copy, not identical" + self.assertEqual(r1.flags, r2.flags) + # make sure meta dict is shallow copied assert r1.meta is not r2.meta, "meta must be a shallow copy, not identical" self.assertEqual(r1.meta, r2.meta) From 7020c3e4523ef445f0d279cdb85fc7570d9e2c2e Mon Sep 17 00:00:00 2001 From: Andrei Korigodski Date: Fri, 20 Jul 2018 14:46:57 +0300 Subject: [PATCH 04/79] Doc: update copyright notice The years are updated. The hyphen is replaced with an en dash. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 594740f39..a54a6bbe9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -45,7 +45,7 @@ master_doc = 'index' # General information about the project. project = u'Scrapy' -copyright = u'2008-2016, Scrapy developers' +copyright = u'2008–2018, Scrapy developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 98d74d1083be6afa7553a5950f89fc5ac446272f Mon Sep 17 00:00:00 2001 From: Eugenio Lacuesta Date: Fri, 20 Jul 2018 12:08:49 -0300 Subject: [PATCH 05/79] Requested changes --- scrapy/extensions/feedexport.py | 29 +++++++++++++++-------------- scrapy/utils/misc.py | 1 + tests/test_feedexport.py | 4 +++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/scrapy/extensions/feedexport.py b/scrapy/extensions/feedexport.py index 067887d94..7c7db387e 100644 --- a/scrapy/extensions/feedexport.py +++ b/scrapy/extensions/feedexport.py @@ -96,21 +96,22 @@ class S3FeedStorage(BlockingFeedStorage): def __init__(self, uri, access_key=None, secret_key=None): # BEGIN Backwards compatibility for initialising without keys (and # without using from_crawler) - from scrapy.conf import settings no_defaults = access_key is None and secret_key is None - if no_defaults and ('AWS_ACCESS_KEY_ID' in settings or - 'AWS_SECRET_ACCESS_KEY' in settings): - import warnings - from scrapy.exceptions import ScrapyDeprecationWarning - warnings.warn( - "Initialising `scrapy.extensions.feedexport.S3FeedStorage` " - "without AWS keys is deprecated. Please supply credentials or " - "use the `from_crawler()` constructor.", - category=ScrapyDeprecationWarning, - stacklevel=2 - ) - access_key = settings['AWS_ACCESS_KEY_ID'] - secret_key = settings['AWS_SECRET_ACCESS_KEY'] + if no_defaults: + from scrapy.conf import settings + if 'AWS_ACCESS_KEY_ID' in settings or 'AWS_SECRET_ACCESS_KEY' in settings: + import warnings + from scrapy.exceptions import ScrapyDeprecationWarning + warnings.warn( + "Initialising `scrapy.extensions.feedexport.S3FeedStorage` " + "without AWS keys is deprecated. Please supply credentials or " + "use the `from_crawler()` constructor.", + category=ScrapyDeprecationWarning, + stacklevel=2 + ) + access_key = settings['AWS_ACCESS_KEY_ID'] + secret_key = settings['AWS_SECRET_ACCESS_KEY'] + # END Backwards compatibility u = urlparse(uri) self.bucketname = u.hostname self.access_key = u.username or access_key diff --git a/scrapy/utils/misc.py b/scrapy/utils/misc.py index 8eb1aabb5..5ccfdcd72 100644 --- a/scrapy/utils/misc.py +++ b/scrapy/utils/misc.py @@ -118,6 +118,7 @@ def rel_has_nofollow(rel): """Return True if link rel attribute has nofollow type""" return True if rel is not None and 'nofollow' in rel.split() else False + def create_instance(objcls, settings, crawler, *args, **kwargs): """Construct a class instance using its ``from_crawler`` or ``from_settings`` constructors, if available. diff --git a/tests/test_feedexport.py b/tests/test_feedexport.py index eeb1bc2a4..380ed971b 100644 --- a/tests/test_feedexport.py +++ b/tests/test_feedexport.py @@ -173,7 +173,9 @@ class S3FeedStorageTest(unittest.TestCase): uri = os.environ.get('S3_TEST_FILE_URI') if not uri: raise unittest.SkipTest("No S3 URI available for testing") - storage = S3FeedStorage(uri, Settings()) + access_key = os.environ.get('AWS_ACCESS_KEY_ID') + secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY') + storage = S3FeedStorage(uri, access_key, secret_key) verifyObject(IFeedStorage, storage) file = storage.open(scrapy.Spider("default")) expected_content = b"content: \xe2\x98\x83" From 48866457b309ebb5edfe2dac8966af1c825b9d65 Mon Sep 17 00:00:00 2001 From: Malcolm Granado Ho Yong Liang Date: Wed, 25 Jul 2018 14:38:37 +0800 Subject: [PATCH 06/79] make amendments to grammer --- docs/contributing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 6615840f7..2369c3436 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -12,7 +12,7 @@ Contributing to Scrapy There are many ways to contribute to Scrapy. Here are some of them: * Blog about Scrapy. Tell the world how you're using Scrapy. This will help - newcomers with more examples and the Scrapy project to increase its + newcomers with more examples and will help the Scrapy project to increase its visibility. * Report bugs and request features in the `issue tracker`_, trying to follow @@ -39,7 +39,7 @@ Reporting bugs trusted Scrapy developers, and its archives are not public. Well-written bug reports are very helpful, so keep in mind the following -guidelines when reporting a new bug. +guidelines when you're going to report a new bug. * check the :ref:`FAQ ` first to see if your issue is addressed in a well-known question From 782f866572d8bf8b1673ea962ae57d7631d1d9de Mon Sep 17 00:00:00 2001 From: CCInCharge Date: Thu, 7 Jun 2018 16:39:48 -0700 Subject: [PATCH 07/79] Fix #3247: Allow scrapy.FormRequest.from_response method to handle duplicate keys --- scrapy/http/request/form.py | 11 ++++++++--- tests/test_http_request.py | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/scrapy/http/request/form.py b/scrapy/http/request/form.py index 95b38e990..c2413b431 100644 --- a/scrapy/http/request/form.py +++ b/scrapy/http/request/form.py @@ -114,10 +114,12 @@ def _get_form(response, formname, formid, formnumber, formxpath): def _get_inputs(form, formdata, dont_click, clickdata, response): try: - formdata = dict(formdata or ()) + formdata_keys = dict(formdata or ()).keys() except (ValueError, TypeError): raise ValueError('formdata should be a dict or iterable of tuples') + if not formdata: + formdata = () inputs = form.xpath('descendant::textarea' '|descendant::select' '|descendant::input[not(@type) or @type[' @@ -128,14 +130,17 @@ def _get_inputs(form, formdata, dont_click, clickdata, response): "re": "http://exslt.org/regular-expressions"}) values = [(k, u'' if v is None else v) for k, v in (_value(e) for e in inputs) - if k and k not in formdata] + if k and k not in formdata_keys] if not dont_click: clickable = _get_clickable(clickdata, form) if clickable and clickable[0] not in formdata and not clickable[0] is None: values.append(clickable) - values.extend((k, v) for k, v in formdata.items() if v is not None) + if isinstance(formdata, dict): + formdata = formdata.items() + + values.extend((k, v) for k, v in formdata if v is not None) return values diff --git a/tests/test_http_request.py b/tests/test_http_request.py index a042f03b6..18fc6413c 100644 --- a/tests/test_http_request.py +++ b/tests/test_http_request.py @@ -401,6 +401,29 @@ class FormRequestTest(RequestTest): self.assertEqual(fs[u'test2'], [u'xxx µ']) self.assertEqual(fs[u'six'], [u'seven']) + def test_from_response_duplicate_form_key(self): + response = _buildresponse( + '
', + url='http://www.example.com') + req = self.request_class.from_response(response, + method='GET', + formdata=(('foo', 'bar'), ('foo', 'baz'))) + self.assertEqual(urlparse(req.url).hostname, 'www.example.com') + self.assertEqual(urlparse(req.url).query, 'foo=bar&foo=baz') + + def test_from_response_override_duplicate_form_key(self): + response = _buildresponse( + """
+ + +
""") + req = self.request_class.from_response( + response, + formdata=(('two', '2'), ('two', '4'))) + fs = _qs(req) + self.assertEqual(fs[b'one'], [b'1']) + self.assertEqual(fs[b'two'], [b'2', b'4']) + def test_from_response_extra_headers(self): response = _buildresponse( """
From 701cd2ff9d4be34fcf63a7410d09f425410635f9 Mon Sep 17 00:00:00 2001 From: Eugenio Lacuesta Date: Mon, 9 Oct 2017 09:42:34 -0300 Subject: [PATCH 08/79] Add from_crawler support to dupefilters --- scrapy/core/scheduler.py | 7 ++++++- tests/test_dupefilters.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/scrapy/core/scheduler.py b/scrapy/core/scheduler.py index a54b4daf0..faed27fd1 100644 --- a/scrapy/core/scheduler.py +++ b/scrapy/core/scheduler.py @@ -26,7 +26,12 @@ class Scheduler(object): def from_crawler(cls, crawler): settings = crawler.settings dupefilter_cls = load_object(settings['DUPEFILTER_CLASS']) - dupefilter = dupefilter_cls.from_settings(settings) + if hasattr(dupefilter_cls, 'from_crawler'): + dupefilter = dupefilter_cls.from_crawler(crawler) + elif hasattr(dupefilter_cls, 'from_settings'): + dupefilter = dupefilter_cls.from_settings(crawler.settings) + else: + dupefilter = dupefilter_cls() pqclass = load_object(settings['SCHEDULER_PRIORITY_QUEUE']) dqclass = load_object(settings['SCHEDULER_DISK_QUEUE']) mqclass = load_object(settings['SCHEDULER_MEMORY_QUEUE']) diff --git a/tests/test_dupefilters.py b/tests/test_dupefilters.py index 2d1a4bfff..81524fddd 100644 --- a/tests/test_dupefilters.py +++ b/tests/test_dupefilters.py @@ -6,10 +6,31 @@ import shutil from scrapy.dupefilters import RFPDupeFilter from scrapy.http import Request from scrapy.utils.python import to_bytes +from scrapy.utils.job import job_dir +from scrapy.utils.test import get_crawler class RFPDupeFilterTest(unittest.TestCase): + def test_dupefilter_from_crawler(self): + + class FromCrawlerRFPDupeFilter(RFPDupeFilter): + + @classmethod + def from_crawler(cls, crawler): + debug = crawler.settings.getbool('DUPEFILTER_DEBUG') + df = cls(job_dir(crawler.settings), debug) + df.user_agent = crawler.settings.get('USER_AGENT') + return df + + crawler = get_crawler(settings_dict={'DUPEFILTER_DEBUG': True, 'USER_AGENT': 'test ua'}) + dupefilter = FromCrawlerRFPDupeFilter.from_crawler(crawler) + + self.assertTrue(dupefilter.debug) + self.assertEqual(dupefilter.user_agent, 'test ua') + + dupefilter.close('finished') + def test_filter(self): dupefilter = RFPDupeFilter() dupefilter.open() From d306fe30ac08401e74fd20cf90e5164a4125d8e1 Mon Sep 17 00:00:00 2001 From: Eugenio Lacuesta Date: Sat, 30 Dec 2017 22:49:22 -0300 Subject: [PATCH 09/79] Test dupefilter creation by the Scheduler --- tests/test_dupefilters.py | 52 ++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/tests/test_dupefilters.py b/tests/test_dupefilters.py index 81524fddd..990b5141c 100644 --- a/tests/test_dupefilters.py +++ b/tests/test_dupefilters.py @@ -5,31 +5,49 @@ import shutil from scrapy.dupefilters import RFPDupeFilter from scrapy.http import Request +from scrapy.core.scheduler import Scheduler from scrapy.utils.python import to_bytes from scrapy.utils.job import job_dir from scrapy.utils.test import get_crawler +class FromCrawlerRFPDupeFilter(RFPDupeFilter): + + @classmethod + def from_crawler(cls, crawler): + debug = crawler.settings.getbool('DUPEFILTER_DEBUG') + df = cls(job_dir(crawler.settings), debug) + df.method = crawler.settings.get('METHOD') + return df + + +class FromSettingsRFPDupeFilter(RFPDupeFilter): + + @classmethod + def from_settings(cls, settings): + debug = settings.getbool('DUPEFILTER_DEBUG') + df = cls(job_dir(settings), debug) + df.method = settings.get('METHOD') + return df + + class RFPDupeFilterTest(unittest.TestCase): - def test_dupefilter_from_crawler(self): + def test_from_crawler_scheduler(self): + settings = {'DUPEFILTER_DEBUG': True, 'METHOD': 'from_crawler', + 'DUPEFILTER_CLASS': __name__ + '.FromCrawlerRFPDupeFilter'} + crawler = get_crawler(settings_dict=settings) + scheduler = Scheduler.from_crawler(crawler) + self.assertTrue(scheduler.df.debug) + self.assertEqual(scheduler.df.method, 'from_crawler') - class FromCrawlerRFPDupeFilter(RFPDupeFilter): - - @classmethod - def from_crawler(cls, crawler): - debug = crawler.settings.getbool('DUPEFILTER_DEBUG') - df = cls(job_dir(crawler.settings), debug) - df.user_agent = crawler.settings.get('USER_AGENT') - return df - - crawler = get_crawler(settings_dict={'DUPEFILTER_DEBUG': True, 'USER_AGENT': 'test ua'}) - dupefilter = FromCrawlerRFPDupeFilter.from_crawler(crawler) - - self.assertTrue(dupefilter.debug) - self.assertEqual(dupefilter.user_agent, 'test ua') - - dupefilter.close('finished') + def test_from_settings_scheduler(self): + settings = {'DUPEFILTER_DEBUG': True, 'METHOD': 'from_settings', + 'DUPEFILTER_CLASS': __name__ + '.FromSettingsRFPDupeFilter'} + crawler = get_crawler(settings_dict=settings) + scheduler = Scheduler.from_crawler(crawler) + self.assertTrue(scheduler.df.debug) + self.assertEqual(scheduler.df.method, 'from_settings') def test_filter(self): dupefilter = RFPDupeFilter() From 0089a4ab31d1764dd38c30c4448a4c62efd9b9c3 Mon Sep 17 00:00:00 2001 From: Eugenio Lacuesta Date: Fri, 23 Mar 2018 13:19:31 -0300 Subject: [PATCH 10/79] Add test for direct creation of dupefilter (no from_crawler/from_settings) --- tests/test_dupefilters.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/test_dupefilters.py b/tests/test_dupefilters.py index 990b5141c..02a01fc94 100644 --- a/tests/test_dupefilters.py +++ b/tests/test_dupefilters.py @@ -17,7 +17,7 @@ class FromCrawlerRFPDupeFilter(RFPDupeFilter): def from_crawler(cls, crawler): debug = crawler.settings.getbool('DUPEFILTER_DEBUG') df = cls(job_dir(crawler.settings), debug) - df.method = crawler.settings.get('METHOD') + df.method = 'from_crawler' return df @@ -27,28 +27,40 @@ class FromSettingsRFPDupeFilter(RFPDupeFilter): def from_settings(cls, settings): debug = settings.getbool('DUPEFILTER_DEBUG') df = cls(job_dir(settings), debug) - df.method = settings.get('METHOD') + df.method = 'from_settings' return df +class DirectRFPDupeFilter(RFPDupeFilter): + method = 'n/a' + + class RFPDupeFilterTest(unittest.TestCase): - def test_from_crawler_scheduler(self): - settings = {'DUPEFILTER_DEBUG': True, 'METHOD': 'from_crawler', + def test_df_from_crawler_scheduler(self): + settings = {'DUPEFILTER_DEBUG': True, 'DUPEFILTER_CLASS': __name__ + '.FromCrawlerRFPDupeFilter'} crawler = get_crawler(settings_dict=settings) scheduler = Scheduler.from_crawler(crawler) self.assertTrue(scheduler.df.debug) self.assertEqual(scheduler.df.method, 'from_crawler') - def test_from_settings_scheduler(self): - settings = {'DUPEFILTER_DEBUG': True, 'METHOD': 'from_settings', + def test_df_from_settings_scheduler(self): + settings = {'DUPEFILTER_DEBUG': True, 'DUPEFILTER_CLASS': __name__ + '.FromSettingsRFPDupeFilter'} crawler = get_crawler(settings_dict=settings) scheduler = Scheduler.from_crawler(crawler) self.assertTrue(scheduler.df.debug) self.assertEqual(scheduler.df.method, 'from_settings') + def test_df_direct_scheduler(self): + settings = {'DUPEFILTER_DEBUG': True, + 'DUPEFILTER_CLASS': __name__ + '.DirectRFPDupeFilter'} + crawler = get_crawler(settings_dict=settings) + scheduler = Scheduler.from_crawler(crawler) + self.assertTrue(scheduler.df.debug) + self.assertEqual(scheduler.df.method, 'n/a') + def test_filter(self): dupefilter = RFPDupeFilter() dupefilter.open() From 9e14f8c7e4141fd5216efc378c9d003ea981a4d5 Mon Sep 17 00:00:00 2001 From: Eugenio Lacuesta Date: Fri, 23 Mar 2018 21:19:57 -0300 Subject: [PATCH 11/79] Fix test for dupefilter --- tests/test_dupefilters.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_dupefilters.py b/tests/test_dupefilters.py index 02a01fc94..db69597a2 100644 --- a/tests/test_dupefilters.py +++ b/tests/test_dupefilters.py @@ -31,7 +31,7 @@ class FromSettingsRFPDupeFilter(RFPDupeFilter): return df -class DirectRFPDupeFilter(RFPDupeFilter): +class DirectDupeFilter(object): method = 'n/a' @@ -54,11 +54,9 @@ class RFPDupeFilterTest(unittest.TestCase): self.assertEqual(scheduler.df.method, 'from_settings') def test_df_direct_scheduler(self): - settings = {'DUPEFILTER_DEBUG': True, - 'DUPEFILTER_CLASS': __name__ + '.DirectRFPDupeFilter'} + settings = {'DUPEFILTER_CLASS': __name__ + '.DirectDupeFilter'} crawler = get_crawler(settings_dict=settings) scheduler = Scheduler.from_crawler(crawler) - self.assertTrue(scheduler.df.debug) self.assertEqual(scheduler.df.method, 'n/a') def test_filter(self): From 999341b60bd5289ca97c8187d0ff380a8555ba5e Mon Sep 17 00:00:00 2001 From: Eugenio Lacuesta Date: Fri, 20 Jul 2018 22:17:55 -0300 Subject: [PATCH 12/79] Simplify dupefilter creation --- scrapy/core/scheduler.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scrapy/core/scheduler.py b/scrapy/core/scheduler.py index faed27fd1..eb790a67e 100644 --- a/scrapy/core/scheduler.py +++ b/scrapy/core/scheduler.py @@ -4,7 +4,7 @@ import logging from os.path import join, exists from scrapy.utils.reqser import request_to_dict, request_from_dict -from scrapy.utils.misc import load_object +from scrapy.utils.misc import load_object, create_instance from scrapy.utils.job import job_dir logger = logging.getLogger(__name__) @@ -26,12 +26,7 @@ class Scheduler(object): def from_crawler(cls, crawler): settings = crawler.settings dupefilter_cls = load_object(settings['DUPEFILTER_CLASS']) - if hasattr(dupefilter_cls, 'from_crawler'): - dupefilter = dupefilter_cls.from_crawler(crawler) - elif hasattr(dupefilter_cls, 'from_settings'): - dupefilter = dupefilter_cls.from_settings(crawler.settings) - else: - dupefilter = dupefilter_cls() + dupefilter = create_instance(dupefilter_cls, settings, crawler) pqclass = load_object(settings['SCHEDULER_PRIORITY_QUEUE']) dqclass = load_object(settings['SCHEDULER_DISK_QUEUE']) mqclass = load_object(settings['SCHEDULER_MEMORY_QUEUE']) From 980be4cb4b70c71e5502c8f31c85882540f3f078 Mon Sep 17 00:00:00 2001 From: Kevin Lloyd Bernal Date: Tue, 31 Jul 2018 19:05:04 +0800 Subject: [PATCH 13/79] provide better error message when disabling s3 exporter --- scrapy/extensions/feedexport.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scrapy/extensions/feedexport.py b/scrapy/extensions/feedexport.py index 7c7db387e..22ebf3b3f 100644 --- a/scrapy/extensions/feedexport.py +++ b/scrapy/extensions/feedexport.py @@ -269,9 +269,10 @@ class FeedExporter(object): try: self._get_storage(uri) return True - except NotConfigured: - logger.error("Disabled feed storage scheme: %(scheme)s", - {'scheme': scheme}) + except NotConfigured as e: + logger.error("Disabled feed storage scheme: %(scheme)s. " + "Reason: %(reason)s", + {'scheme': scheme, 'reason': str(e)}) else: logger.error("Unknown feed storage scheme: %(scheme)s", {'scheme': scheme}) From c87a4f5c6fd97d22345bb444bc90b56ef0624aa5 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Wed, 1 Aug 2018 01:45:16 +0500 Subject: [PATCH 14/79] remove unused imports from scrapy/settings/__init__.py This is a follow-up to https://github.com/scrapy/scrapy/pull/3327 --- scrapy/settings/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scrapy/settings/__init__.py b/scrapy/settings/__init__.py index 7d6d20164..14c93bef2 100644 --- a/scrapy/settings/__init__.py +++ b/scrapy/settings/__init__.py @@ -1,13 +1,10 @@ import six import json import copy -import warnings from collections import MutableMapping from importlib import import_module from pprint import pformat -from scrapy.exceptions import ScrapyDeprecationWarning - from . import default_settings From d3aa1e86664b02a20522abbb265dc9b91b95f111 Mon Sep 17 00:00:00 2001 From: Raphael Wuillemier Date: Mon, 6 Aug 2018 17:40:31 +0200 Subject: [PATCH 15/79] Updated tutorial.rst to include more and up-to-date beginner resources --- docs/intro/tutorial.rst | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index 20538e90f..0db6a6218 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -26,15 +26,26 @@ If you're already familiar with other languages, and want to learn Python quickly, we recommend reading through `Dive Into Python 3`_. Alternatively, you can follow the `Python Tutorial`_. -If you're new to programming and want to start with Python, you may find useful -the online book `Learn Python The Hard Way`_. You can also take a look at `this -list of Python resources for non-programmers`_. +If you're new to programming and want to start with Python, the following books +may be useful to you: + +* `Automate the Boring Stuff With Python`_ + +* `How To Think Like a Computer Scientist`_ + +* `Learn Python 3 The Hard Way`_ + +You can also take a look at `this list of Python resources for non-programmers`_, +as well as the `suggested resources in the learnpython-subreddit`_. .. _Python: https://www.python.org/ .. _this list of Python resources for non-programmers: https://wiki.python.org/moin/BeginnersGuide/NonProgrammers .. _Dive Into Python 3: http://www.diveintopython3.net .. _Python Tutorial: https://docs.python.org/3/tutorial -.. _Learn Python The Hard Way: https://learnpythonthehardway.org/book/ +.. _Automate the Boring Stuff With Python: https://automatetheboringstuff.com/ +.. _How To Think Like a Computer Scientist: http://openbookproject.net/thinkcs/python/english3e/ +.. _Learn Python 3 The Hard Way: https://learnpythonthehardway.org/python3/ +.. _suggested resources in the learnpython-subreddit: https://www.reddit.com/r/learnpython/wiki/index#wiki_new_to_python.3F Creating a project From 16dad81715d3970149c0cf7a318e73a0d84be1ff Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Thu, 9 Aug 2018 21:07:25 +0300 Subject: [PATCH 16/79] Fix contract errback --- scrapy/contracts/__init__.py | 2 +- tests/test_contracts.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py index 5eaee3d11..8315d21d2 100644 --- a/scrapy/contracts/__init__.py +++ b/scrapy/contracts/__init__.py @@ -84,7 +84,7 @@ class ContractsManager(object): def eb_wrapper(failure): case = _create_testcase(method, 'errback') - exc_info = failure.value, failure.type, failure.getTracebackObject() + exc_info = failure.type, failure.value, failure.getTracebackObject() results.addError(case, exc_info) request.callback = cb_wrapper diff --git a/tests/test_contracts.py b/tests/test_contracts.py index 1cea2afb7..b07cbee1e 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -1,7 +1,9 @@ from unittest import TextTestResult +from twisted.python import failure from twisted.trial import unittest +from scrapy.spidermiddlewares.httperror import HttpError from scrapy.spiders import Spider from scrapy.http import Request from scrapy.item import Item, Field @@ -185,3 +187,18 @@ class ContractsManagerTest(unittest.TestCase): self.results) request.callback(response) self.should_fail() + + def test_errback(self): + spider = TestSpider() + response = ResponseMock() + + try: + raise HttpError(response, 'Ignoring non-200 response') + except HttpError: + failure_mock = failure.Failure() + + request = self.conman.from_method(spider.returns_request, self.results) + request.errback(failure_mock) + + self.assertFalse(self.results.failures) + self.assertTrue(self.results.errors) From fb7d4cbce379c4e3fac2ca89b6e0772d0c690935 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sat, 11 Aug 2018 16:08:26 +0300 Subject: [PATCH 17/79] Add error handling in contracts --- scrapy/contracts/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py index 5eaee3d11..18014e290 100644 --- a/scrapy/contracts/__init__.py +++ b/scrapy/contracts/__init__.py @@ -41,7 +41,11 @@ class ContractsManager(object): requests = [] for method in self.tested_methods_from_spidercls(type(spider)): bound_method = spider.__getattribute__(method) - requests.append(self.from_method(bound_method, results)) + try: + requests.append(self.from_method(bound_method, results)) + except: + case = _create_testcase(bound_method, 'contract') + results.addError(case, sys.exc_info()) return requests From ebbde57eca310d3c5c0f530a2e668572dde4d952 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sat, 11 Aug 2018 17:50:56 +0300 Subject: [PATCH 18/79] Add custom contracts tests --- tests/test_contracts.py | 55 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index 1cea2afb7..078ef6e0d 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -5,7 +5,7 @@ from twisted.trial import unittest from scrapy.spiders import Spider from scrapy.http import Request from scrapy.item import Item, Field -from scrapy.contracts import ContractsManager +from scrapy.contracts import ContractsManager, Contract from scrapy.contracts.default import ( UrlContract, ReturnsContract, @@ -22,6 +22,21 @@ class ResponseMock(object): url = 'http://scrapy.org' +class CustomSuccessContract(Contract): + name = 'custom_success_contract' + + def adjust_request_args(self, args): + args['url'] = 'http://scrapy.org' + return args + + +class CustomFailContract(Contract): + name = 'custom_fail_contract' + + def adjust_request_args(self, args): + raise TypeError('Error in adjust_request_args') + + class TestSpider(Spider): name = 'demo_spider' @@ -99,8 +114,34 @@ class TestSpider(Spider): pass +class CustomContractSuccessSpider(Spider): + name = 'custom_contract_success_spider' + + def parse(self, response): + """ + @custom_success_contract + """ + pass + + +class CustomContractFailSpider(Spider): + name = 'custom_contract_fail_spider' + + def parse(self, response): + """ + @custom_fail_contract + """ + pass + + class ContractsManagerTest(unittest.TestCase): - contracts = [UrlContract, ReturnsContract, ScrapesContract] + contracts = [ + UrlContract, + ReturnsContract, + ScrapesContract, + CustomSuccessContract, + CustomFailContract + ] def setUp(self): self.conman = ContractsManager(self.contracts) @@ -114,6 +155,9 @@ class ContractsManagerTest(unittest.TestCase): self.assertTrue(self.results.failures) self.assertFalse(self.results.errors) + def should_error(self): + self.assertTrue(self.results.errors) + def test_contracts(self): spider = TestSpider() @@ -185,3 +229,10 @@ class ContractsManagerTest(unittest.TestCase): self.results) request.callback(response) self.should_fail() + + def test_custom_contracts(self): + self.conman.from_spider(CustomContractSuccessSpider(), self.results) + self.should_succeed() + + self.conman.from_spider(CustomContractFailSpider(), self.results) + self.should_error() From 76220e8733b1462ecaa5db1cdd4a58ad4e93ceb3 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sat, 11 Aug 2018 18:49:12 +0300 Subject: [PATCH 19/79] Use inspect.getmembers in tested_methods_from_spidercls --- scrapy/contracts/__init__.py | 3 ++- tests/test_contracts.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py index 8315d21d2..de7ac4503 100644 --- a/scrapy/contracts/__init__.py +++ b/scrapy/contracts/__init__.py @@ -1,6 +1,7 @@ import sys import re from functools import wraps +from inspect import getmembers from unittest import TestCase from scrapy.http import Request @@ -17,7 +18,7 @@ class ContractsManager(object): def tested_methods_from_spidercls(self, spidercls): methods = [] - for key, value in vars(spidercls).items(): + for key, value in getmembers(spidercls): if (callable(value) and value.__doc__ and re.search(r'^\s*@', value.__doc__, re.MULTILINE)): methods.append(key) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index b07cbee1e..322d20c4c 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -101,6 +101,10 @@ class TestSpider(Spider): pass +class InheritsTestSpider(TestSpider): + name = 'inherits_demo_spider' + + class ContractsManagerTest(unittest.TestCase): contracts = [UrlContract, ReturnsContract, ScrapesContract] @@ -202,3 +206,9 @@ class ContractsManagerTest(unittest.TestCase): self.assertFalse(self.results.failures) self.assertTrue(self.results.errors) + + def test_inherited_contracts(self): + spider = InheritsTestSpider() + + requests = self.conman.from_spider(spider, self.results) + self.assertTrue(requests) From 8fc017d345af1e6fa53334fc9df92ebd8f9dc32e Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sat, 11 Aug 2018 19:25:33 +0300 Subject: [PATCH 20/79] Add dont_filter to ContractsManager requests --- scrapy/contracts/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py index 8315d21d2..2569ab151 100644 --- a/scrapy/contracts/__init__.py +++ b/scrapy/contracts/__init__.py @@ -50,7 +50,12 @@ class ContractsManager(object): if contracts: # calculate request args args, kwargs = get_spec(Request.__init__) + + # Don't filter requests to allow + # testing different callbacks on the same URL. + kwargs['dont_filter'] = True kwargs['callback'] = method + for contract in contracts: kwargs = contract.adjust_request_args(kwargs) From b4b1e4834376e8565d727a1a5087cae31450f931 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sat, 11 Aug 2018 22:18:43 +0300 Subject: [PATCH 21/79] Add ability to use FormRequest in contracts --- docs/topics/contracts.rst | 7 +++++-- scrapy/contracts/__init__.py | 7 ++++++- tests/test_contracts.py | 26 ++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/docs/topics/contracts.rst b/docs/topics/contracts.rst index ba1421c42..cac52042a 100644 --- a/docs/topics/contracts.rst +++ b/docs/topics/contracts.rst @@ -86,8 +86,11 @@ override three methods: .. method:: Contract.adjust_request_args(args) This receives a ``dict`` as an argument containing default arguments - for :class:`~scrapy.http.Request` object. Must return the same or a - modified version of it. + for request object. If ``formdata`` is in ``args``, then + :class:`~scrapy.http.FormRequest` object is created, + otherwise :class:`~scrapy.http.Request` is used. + + Must return the same or a modified version of it. .. method:: Contract.pre_process(response) diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py index 8315d21d2..ca2a8d384 100644 --- a/scrapy/contracts/__init__.py +++ b/scrapy/contracts/__init__.py @@ -3,6 +3,7 @@ import re from functools import wraps from unittest import TestCase +from scrapy import FormRequest from scrapy.http import Request from scrapy.utils.spider import iterate_spider_output from scrapy.utils.python import get_spec @@ -57,7 +58,11 @@ class ContractsManager(object): # create and prepare request args.remove('self') if set(args).issubset(set(kwargs)): - request = Request(**kwargs) + if 'formdata' in kwargs: + kwargs['method'] = 'POST' + request = FormRequest(**kwargs) + else: + request = Request(**kwargs) # execute pre and post hooks in order for contract in reversed(contracts): diff --git a/tests/test_contracts.py b/tests/test_contracts.py index b07cbee1e..f2085f711 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -3,11 +3,12 @@ from unittest import TextTestResult from twisted.python import failure from twisted.trial import unittest +from scrapy import FormRequest from scrapy.spidermiddlewares.httperror import HttpError from scrapy.spiders import Spider from scrapy.http import Request from scrapy.item import Item, Field -from scrapy.contracts import ContractsManager +from scrapy.contracts import ContractsManager, Contract from scrapy.contracts.default import ( UrlContract, ReturnsContract, @@ -24,6 +25,14 @@ class ResponseMock(object): url = 'http://scrapy.org' +class CustomFormContract(Contract): + name = 'custom_form' + + def adjust_request_args(self, args): + args['formdata'] = {'name': 'scrapy'} + return args + + class TestSpider(Spider): name = 'demo_spider' @@ -100,9 +109,16 @@ class TestSpider(Spider): """ pass + def custom_form(self, response): + """ + @url http://scrapy.org + @custom_form + """ + pass + class ContractsManagerTest(unittest.TestCase): - contracts = [UrlContract, ReturnsContract, ScrapesContract] + contracts = [UrlContract, ReturnsContract, ScrapesContract, CustomFormContract] def setUp(self): self.conman = ContractsManager(self.contracts) @@ -202,3 +218,9 @@ class ContractsManagerTest(unittest.TestCase): self.assertFalse(self.results.failures) self.assertTrue(self.results.errors) + + def test_form_contract(self): + spider = TestSpider() + request = self.conman.from_method(spider.custom_form, self.results) + self.assertEqual(request.method, 'POST') + self.assertIsInstance(request, FormRequest) From 1d25c98eb337763f613d73fd534d3dc5e64eb66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Tue, 3 Jul 2018 16:41:53 -0300 Subject: [PATCH 22/79] Add appveyor.yml --- appveyor.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..81432be39 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,14 @@ +platform: x86 +version: '{branch}-{build}' +environment: + matrix: + - PYTHON: "C:\\Python36" + TOX_ENV: py36 + +install: + - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + - "SET TOX_TESTENV_PASSENV=HOME USERPROFILE HOMEPATH HOMEDRIVE" + - "pip install -U tox twine wheel" +build: false +test_script: + - "tox -e %TOX_ENV%" From 4c53957f5bd0f64b00d9488f171f0f8a9620af02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Tue, 3 Jul 2018 16:56:05 -0300 Subject: [PATCH 23/79] Skip leveldb tests on windows --- tests/requirements-py3.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/requirements-py3.txt b/tests/requirements-py3.txt index 8d9ce5231..1e4a4b641 100644 --- a/tests/requirements-py3.txt +++ b/tests/requirements-py3.txt @@ -3,7 +3,7 @@ pytest-twisted pytest-cov==2.5.1 testfixtures jmespath -leveldb +leveldb; sys_platform != "win32" botocore # optional for shell wrapper tests bpython From dd75297e3fb37800c0ef763003a54081e9fad4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Tue, 3 Jul 2018 16:58:02 -0300 Subject: [PATCH 24/79] Run Appveyor CI for master and release branches only, but also PRs --- appveyor.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 81432be39..4f3c69847 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,6 +5,11 @@ environment: - PYTHON: "C:\\Python36" TOX_ENV: py36 +branches: + only: + - master + - /d+\.\d+\.\d+[\w\-]*$/ + install: - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - "SET TOX_TESTENV_PASSENV=HOME USERPROFILE HOMEPATH HOMEDRIVE" From 19ad94105f70a26ce47683d488b66fb77b79067b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Tue, 3 Jul 2018 17:15:48 -0300 Subject: [PATCH 25/79] pywin32 is required to run tests under windows --- tests/requirements-py3.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/requirements-py3.txt b/tests/requirements-py3.txt index 1e4a4b641..7c1aacd81 100644 --- a/tests/requirements-py3.txt +++ b/tests/requirements-py3.txt @@ -9,3 +9,4 @@ botocore bpython ipython brotlipy +pywin32; sys_platform == "win32" From 152fde70b12f3c6e0e73230370f79f4b4a7ea906 Mon Sep 17 00:00:00 2001 From: Jakob de Maeyer Date: Tue, 2 Feb 2016 18:23:23 +0000 Subject: [PATCH 26/79] Fix FTPTestCase by using Windows-friendly temporary file name --- tests/test_downloader_handlers.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_downloader_handlers.py b/tests/test_downloader_handlers.py index c91be2c0c..fe76989f4 100644 --- a/tests/test_downloader_handlers.py +++ b/tests/test_downloader_handlers.py @@ -1,7 +1,8 @@ import os import six -import contextlib import shutil +import tempfile +import contextlib try: from unittest import mock except ImportError: @@ -913,7 +914,8 @@ class BaseFTPTestCase(unittest.TestCase): return self._add_test_callbacks(d, _test) def test_ftp_local_filename(self): - local_fname = b"/tmp/file.txt" + f, local_fname = tempfile.mkstemp() + os.close(f) meta = {"ftp_local_filename": local_fname} meta.update(self.req_meta) request = Request(url="ftp://127.0.0.1:%s/file.txt" % self.portNum, @@ -922,7 +924,8 @@ class BaseFTPTestCase(unittest.TestCase): def _test(r): self.assertEqual(r.body, local_fname) - self.assertEqual(r.headers, {b'Local Filename': [b'/tmp/file.txt'], b'Size': [b'17']}) + self.assertEqual(r.headers, {b'Local Filename': [local_fname], + b'Size': [b'17']}) self.assertTrue(os.path.exists(local_fname)) with open(local_fname, "rb") as f: self.assertEqual(f.read(), b"I have the power!") From 57a1d66c61423c85703a4008d351e9d566580810 Mon Sep 17 00:00:00 2001 From: Jakob de Maeyer Date: Wed, 3 Feb 2016 16:56:43 +0000 Subject: [PATCH 27/79] Fix test issues caused by Windows pipe buffer filling up --- tests/test_commands.py | 48 +++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 7d9071b64..84c38c0e9 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -3,18 +3,17 @@ import os import sys import subprocess import tempfile -from time import sleep from os.path import exists, join, abspath from shutil import rmtree, copytree from tempfile import mkdtemp from contextlib import contextmanager +from threading import Timer from twisted.trial import unittest from twisted.internet import defer import scrapy from scrapy.utils.python import to_native_str -from scrapy.utils.python import retry_on_eintr from scrapy.utils.test import get_testenv from scrapy.utils.testsite import SiteTest from scrapy.utils.testproc import ProcessTest @@ -46,16 +45,18 @@ class ProjectTest(unittest.TestCase): stdout=subprocess.PIPE, stderr=subprocess.PIPE, **popen_kwargs) - waited = 0 - interval = 0.2 - while p.poll() is None: - sleep(interval) - waited += interval - if waited > 15: - p.kill() - assert False, 'Command took too much time to complete' + def kill_proc(): + p.kill() + assert False, 'Command took too much time to complete' - return p + timer = Timer(15, kill_proc) + try: + timer.start() + stdout, stderr = p.communicate() + finally: + timer.cancel() + + return to_native_str(stdout), to_native_str(stderr) class StartprojectTest(ProjectTest): @@ -111,8 +112,7 @@ class StartprojectTemplatesTest(ProjectTest): assert exists(join(self.tmpl_proj, 'root_template')) args = ['--set', 'TEMPLATES_DIR=%s' % self.tmpl] - p = self.proc('startproject', self.project_name, *args) - out = to_native_str(retry_on_eintr(p.stdout.read)) + out, err = self.proc('startproject', self.project_name, *args) self.assertIn("New Scrapy project %r, using template directory" % self.project_name, out) self.assertIn(self.tmpl_proj, out) assert exists(join(self.proj_path, 'root_template')) @@ -140,12 +140,10 @@ class GenspiderCommandTest(CommandTest): def test_template(self, tplname='crawl'): args = ['--template=%s' % tplname] if tplname else [] spname = 'test_spider' - p = self.proc('genspider', spname, 'test.com', *args) - out = to_native_str(retry_on_eintr(p.stdout.read)) + out, err = self.proc('genspider', spname, 'test.com', *args) self.assertIn("Created spider %r using template %r in module" % (spname, tplname), out) self.assertTrue(exists(join(self.proj_mod_path, 'spiders', 'test_spider.py'))) - p = self.proc('genspider', spname, 'test.com', *args) - out = to_native_str(retry_on_eintr(p.stdout.read)) + out, err = self.proc('genspider', spname, 'test.com', *args) self.assertIn("Spider %r already exists in module" % spname, out) def test_template_basic(self): @@ -212,8 +210,8 @@ class MySpider(scrapy.Spider): return self.proc('runspider', fname, *args) def get_log(self, code, name='myspider.py', args=()): - p = self.runspider(code, name=name, args=args) - return to_native_str(p.stderr.read()) + stdout, stderr = self.runspider(code, name=name, args=args) + return stderr def test_runspider(self): log = self.get_log(self.debug_log_spider) @@ -279,14 +277,17 @@ class MySpider(scrapy.Spider): self.assertIn("No spider found in file", log) def test_runspider_file_not_found(self): - p = self.proc('runspider', 'some_non_existent_file') - log = to_native_str(p.stderr.read()) + _, log = self.proc('runspider', 'some_non_existent_file') self.assertIn("File not found: some_non_existent_file", log) def test_runspider_unable_to_load(self): log = self.get_log('', name='myspider.txt') self.assertIn('Unable to load', log) + +class ParseCommandTest(ProcessTest, SiteTest, CommandTest): + command = 'parse' + def test_start_requests_errors(self): log = self.get_log(""" import scrapy @@ -304,8 +305,7 @@ class BadSpider(scrapy.Spider): class BenchCommandTest(CommandTest): def test_run(self): - p = self.proc('bench', '-s', 'LOGSTATS_INTERVAL=0.001', - '-s', 'CLOSESPIDER_TIMEOUT=0.01') - log = to_native_str(p.stderr.read()) + _, log = self.proc('bench', '-s', 'LOGSTATS_INTERVAL=0.001', + '-s', 'CLOSESPIDER_TIMEOUT=0.01') self.assertIn('INFO: Crawled', log) self.assertNotIn('Unhandled Error', log) From ed8255bde04d7e07747792022858f161c2296096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Fri, 6 Jul 2018 17:53:56 -0300 Subject: [PATCH 28/79] Fix merge issues with stderr/out fixes for windows buffering --- tests/test_commands.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 84c38c0e9..4963ef99c 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -56,7 +56,7 @@ class ProjectTest(unittest.TestCase): finally: timer.cancel() - return to_native_str(stdout), to_native_str(stderr) + return p, to_native_str(stdout), to_native_str(stderr) class StartprojectTest(ProjectTest): @@ -112,7 +112,7 @@ class StartprojectTemplatesTest(ProjectTest): assert exists(join(self.tmpl_proj, 'root_template')) args = ['--set', 'TEMPLATES_DIR=%s' % self.tmpl] - out, err = self.proc('startproject', self.project_name, *args) + p, out, err = self.proc('startproject', self.project_name, *args) self.assertIn("New Scrapy project %r, using template directory" % self.project_name, out) self.assertIn(self.tmpl_proj, out) assert exists(join(self.proj_path, 'root_template')) @@ -140,10 +140,10 @@ class GenspiderCommandTest(CommandTest): def test_template(self, tplname='crawl'): args = ['--template=%s' % tplname] if tplname else [] spname = 'test_spider' - out, err = self.proc('genspider', spname, 'test.com', *args) + p, out, err = self.proc('genspider', spname, 'test.com', *args) self.assertIn("Created spider %r using template %r in module" % (spname, tplname), out) self.assertTrue(exists(join(self.proj_mod_path, 'spiders', 'test_spider.py'))) - out, err = self.proc('genspider', spname, 'test.com', *args) + p, out, err = self.proc('genspider', spname, 'test.com', *args) self.assertIn("Spider %r already exists in module" % spname, out) def test_template_basic(self): @@ -210,7 +210,7 @@ class MySpider(scrapy.Spider): return self.proc('runspider', fname, *args) def get_log(self, code, name='myspider.py', args=()): - stdout, stderr = self.runspider(code, name=name, args=args) + p, stdout, stderr = self.runspider(code, name=name, args=args) return stderr def test_runspider(self): @@ -221,12 +221,12 @@ class MySpider(scrapy.Spider): self.assertIn("INFO: Spider closed (finished)", log) def test_run_fail_spider(self): - proc = self.runspider("import scrapy\n" + inspect.getsource(ExceptionSpider)) + proc, _, _ = self.runspider("import scrapy\n" + inspect.getsource(ExceptionSpider)) ret = proc.returncode self.assertNotEqual(ret, 0) def test_run_good_spider(self): - proc = self.runspider("import scrapy\n" + inspect.getsource(NoRequestsSpider)) + proc, _, _ = self.runspider("import scrapy\n" + inspect.getsource(NoRequestsSpider)) ret = proc.returncode self.assertEqual(ret, 0) @@ -277,7 +277,7 @@ class MySpider(scrapy.Spider): self.assertIn("No spider found in file", log) def test_runspider_file_not_found(self): - _, log = self.proc('runspider', 'some_non_existent_file') + _, _, log = self.proc('runspider', 'some_non_existent_file') self.assertIn("File not found: some_non_existent_file", log) def test_runspider_unable_to_load(self): @@ -305,7 +305,7 @@ class BadSpider(scrapy.Spider): class BenchCommandTest(CommandTest): def test_run(self): - _, log = self.proc('bench', '-s', 'LOGSTATS_INTERVAL=0.001', + _, _, log = self.proc('bench', '-s', 'LOGSTATS_INTERVAL=0.001', '-s', 'CLOSESPIDER_TIMEOUT=0.01') self.assertIn('INFO: Crawled', log) self.assertNotIn('Unhandled Error', log) From 034152961d187930eb659bef9e87ceb3e589c103 Mon Sep 17 00:00:00 2001 From: Jakob de Maeyer Date: Wed, 3 Feb 2016 18:12:08 +0000 Subject: [PATCH 29/79] Fix Feedexport test in Windows by using proper file URI --- tests/test_feedexport.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_feedexport.py b/tests/test_feedexport.py index 2b57449d9..6eefa14bf 100644 --- a/tests/test_feedexport.py +++ b/tests/test_feedexport.py @@ -2,11 +2,12 @@ from __future__ import absolute_import import os import csv import json +import warnings from io import BytesIO import tempfile import shutil -from six.moves.urllib.parse import urlparse -import warnings +from six.moves.urllib.parse import urljoin, urlparse +from six.moves.urllib.request import pathname2url from zope.interface.verify import verifyObject from twisted.trial import unittest @@ -226,9 +227,10 @@ class FeedExportTest(unittest.TestCase): def run_and_export(self, spider_cls, settings=None): """ Run spider with specified settings; return exported data. """ tmpdir = tempfile.mkdtemp() - res_name = tmpdir + '/res' + res_path = os.path.join(tmpdir, 'res') + res_uri = urljoin('file:', pathname2url(res_path)) defaults = { - 'FEED_URI': 'file://' + res_name, + 'FEED_URI': res_uri, 'FEED_FORMAT': 'csv', } defaults.update(settings or {}) @@ -238,7 +240,7 @@ class FeedExportTest(unittest.TestCase): spider_cls.start_urls = [s.url('/')] yield runner.crawl(spider_cls) - with open(res_name, 'rb') as f: + with open(res_path, 'rb') as f: defer.returnValue(f.read()) finally: From 22505a34a9d0095dfc6e133bbb79d3b7aa651082 Mon Sep 17 00:00:00 2001 From: Jakob de Maeyer Date: Wed, 3 Feb 2016 18:42:51 +0000 Subject: [PATCH 30/79] Fix cmdline profiling test on Windows by using proper path composing --- tests/test_cmdline/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_cmdline/__init__.py b/tests/test_cmdline/__init__.py index 10076bbca..68dfb1cca 100644 --- a/tests/test_cmdline/__init__.py +++ b/tests/test_cmdline/__init__.py @@ -52,7 +52,8 @@ class CmdlineTest(unittest.TestCase): stats.print_stats() out.seek(0) stats = out.read() - self.assertIn('scrapy/commands/version.py', stats) + self.assertIn(os.path.join('scrapy', 'commands', 'version.py'), + stats) self.assertIn('tottime', stats) finally: shutil.rmtree(path) From fb09148c91f118e3a71243e527a0d39294ba59cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Fri, 6 Jul 2018 18:10:56 -0300 Subject: [PATCH 31/79] Fix bad merge on ParseCommandTest --- tests/test_commands.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 4963ef99c..78aa2a776 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -284,10 +284,6 @@ class MySpider(scrapy.Spider): log = self.get_log('', name='myspider.txt') self.assertIn('Unable to load', log) - -class ParseCommandTest(ProcessTest, SiteTest, CommandTest): - command = 'parse' - def test_start_requests_errors(self): log = self.get_log(""" import scrapy From a21abac743865b628e52f11882db7b7ab70e0342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Sat, 7 Jul 2018 10:15:20 -0300 Subject: [PATCH 32/79] fix ftp tests on windows --- tests/test_downloader_handlers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_downloader_handlers.py b/tests/test_downloader_handlers.py index fe76989f4..2f8973054 100644 --- a/tests/test_downloader_handlers.py +++ b/tests/test_downloader_handlers.py @@ -915,6 +915,7 @@ class BaseFTPTestCase(unittest.TestCase): def test_ftp_local_filename(self): f, local_fname = tempfile.mkstemp() + local_fname = to_bytes(local_fname) os.close(f) meta = {"ftp_local_filename": local_fname} meta.update(self.req_meta) From ed068e59b746de6395c33f6f0f5454e69e88d1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Sat, 7 Jul 2018 10:28:07 -0300 Subject: [PATCH 33/79] Cache pip cache and do not rebuild tags on appveyor and travis --- appveyor.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4f3c69847..93cfd469e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,12 @@ branches: install: - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - "SET TOX_TESTENV_PASSENV=HOME USERPROFILE HOMEPATH HOMEDRIVE" - - "pip install -U tox twine wheel" + - "pip install -U tox" + build: false +skip_tags: true test_script: - "tox -e %TOX_ENV%" + +cache: + - '%LOCALAPPDATA%\pip\cache' From 0e532e3dd8b4dd07b1a287d6321c1e6ae6786c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Sat, 7 Jul 2018 10:46:15 -0300 Subject: [PATCH 34/79] Creating a connection to 0.0.0.0 fails on windows but not on linux nor mac --- tests/mockserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mockserver.py b/tests/mockserver.py index f36ce3c44..bf62fe907 100644 --- a/tests/mockserver.py +++ b/tests/mockserver.py @@ -209,7 +209,7 @@ class MockServer(): time.sleep(0.2) def url(self, path, is_secure=False): - host = self.http_address + host = self.http_address.replace('0.0.0.0', '127.0.0.1') if is_secure: host = self.https_address return host + path From ca53a8699a8cfc2a0a3be4a65d91bada8a43f94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Sat, 7 Jul 2018 11:07:05 -0300 Subject: [PATCH 35/79] Fix presentation of template directory in startproject command --- scrapy/commands/startproject.py | 4 ++-- tests/test_commands.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scrapy/commands/startproject.py b/scrapy/commands/startproject.py index c17aaf442..67337c26e 100644 --- a/scrapy/commands/startproject.py +++ b/scrapy/commands/startproject.py @@ -107,8 +107,8 @@ class Command(ScrapyCommand): string.Template(path).substitute(project_name=project_name)) render_templatefile(tplfile, project_name=project_name, ProjectName=string_camelcase(project_name)) - print("New Scrapy project %r, using template directory %r, created in:" % \ - (project_name, self.templates_dir)) + print("New Scrapy project '%s', using template directory '%s', " + "created in:" % (project_name, self.templates_dir)) print(" %s\n" % abspath(project_dir)) print("You can start your first spider with:") print(" cd %s" % project_dir) diff --git a/tests/test_commands.py b/tests/test_commands.py index 78aa2a776..b8445ae6c 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -113,7 +113,8 @@ class StartprojectTemplatesTest(ProjectTest): args = ['--set', 'TEMPLATES_DIR=%s' % self.tmpl] p, out, err = self.proc('startproject', self.project_name, *args) - self.assertIn("New Scrapy project %r, using template directory" % self.project_name, out) + self.assertIn("New Scrapy project '%s', using template directory" + % self.project_name, out) self.assertIn(self.tmpl_proj, out) assert exists(join(self.proj_path, 'root_template')) From cb281757500b2e114fb554aa3bf618e3a8f8eb71 Mon Sep 17 00:00:00 2001 From: Jakob de Maeyer Date: Wed, 3 Feb 2016 18:35:35 +0000 Subject: [PATCH 36/79] Fix csviter tests by explicitly using newline only --- tests/test_utils_iterators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_utils_iterators.py b/tests/test_utils_iterators.py index b2e8610f8..f953076b8 100644 --- a/tests/test_utils_iterators.py +++ b/tests/test_utils_iterators.py @@ -7,7 +7,7 @@ from scrapy.utils.iterators import csviter, xmliter, _body_or_str, xmliter_lxml from scrapy.http import XmlResponse, TextResponse, Response from tests import get_testdata -FOOBAR_NL = u"foo" + os.linesep + u"bar" +FOOBAR_NL = u"foo\nbar" class XmliterTestCase(unittest.TestCase): From d93d960319e22badccd68499df11f2a728dbbc04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Wed, 15 Aug 2018 01:53:20 -0300 Subject: [PATCH 37/79] Fix test_utils_project under Windows --- tests/test_utils_project.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_utils_project.py b/tests/test_utils_project.py index 7e2caace8..bd74b0c34 100644 --- a/tests/test_utils_project.py +++ b/tests/test_utils_project.py @@ -25,8 +25,12 @@ def inside_a_project(): class ProjectUtilsTest(unittest.TestCase): def test_data_path_outside_project(self): - self.assertEqual('.scrapy/somepath', data_path('somepath')) - self.assertEqual('/absolute/path', data_path('/absolute/path')) + self.assertEqual( + os.path.join('.scrapy', 'somepath'), + data_path('somepath') + ) + abspath = os.path.join(os.path.sep, 'absolute', 'path') + self.assertEqual(abspath, data_path(abspath)) def test_data_path_inside_project(self): with inside_a_project() as proj_path: @@ -35,4 +39,5 @@ class ProjectUtilsTest(unittest.TestCase): os.path.realpath(expected), os.path.realpath(data_path('somepath')) ) - self.assertEqual('/absolute/path', data_path('/absolute/path')) + abspath = os.path.join(os.path.sep, 'absolute', 'path') + self.assertEqual(abspath, data_path(abspath)) From 96517cb7de93da990f4f7dcb1cb7b7129e8c2064 Mon Sep 17 00:00:00 2001 From: Daniel Grana Date: Wed, 15 Aug 2018 01:08:40 -0700 Subject: [PATCH 38/79] Fix test_command_parse under windows --- tests/test_command_parse.py | 38 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tests/test_command_parse.py b/tests/test_command_parse.py index 66dd17110..02037b866 100644 --- a/tests/test_command_parse.py +++ b/tests/test_command_parse.py @@ -1,3 +1,4 @@ +import os from os.path import join, abspath from twisted.trial import unittest from twisted.internet import defer @@ -7,6 +8,11 @@ from scrapy.utils.python import to_native_str from tests.test_commands import CommandTest +def _textmode(bstr): + """Normalize input the same as writing to a file + and reading from it in text mode""" + return to_native_str(bstr).replace(os.linesep, '\n') + class ParseCommandTest(ProcessTest, SiteTest, CommandTest): command = 'parse' @@ -97,7 +103,7 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} '-a', 'test_arg=1', '-c', 'parse', self.url('/html')]) - self.assertIn("DEBUG: It Works!", to_native_str(stderr)) + self.assertIn("DEBUG: It Works!", _textmode(stderr)) @defer.inlineCallbacks def test_request_with_meta(self): @@ -106,13 +112,13 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} '--meta', raw_json_string, '-c', 'parse_request_with_meta', self.url('/html')]) - self.assertIn("DEBUG: It Works!", to_native_str(stderr)) + self.assertIn("DEBUG: It Works!", _textmode(stderr)) _, _, stderr = yield self.execute(['--spider', self.spider_name, '-m', raw_json_string, '-c', 'parse_request_with_meta', self.url('/html')]) - self.assertIn("DEBUG: It Works!", to_native_str(stderr)) + self.assertIn("DEBUG: It Works!", _textmode(stderr)) @defer.inlineCallbacks @@ -120,7 +126,7 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} _, _, stderr = yield self.execute(['--spider', self.spider_name, '-c', 'parse_request_without_meta', self.url('/html')]) - self.assertIn("DEBUG: It Works!", to_native_str(stderr)) + self.assertIn("DEBUG: It Works!", _textmode(stderr)) @defer.inlineCallbacks @@ -129,29 +135,29 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} '--pipelines', '-c', 'parse', self.url('/html')]) - self.assertIn("INFO: It Works!", to_native_str(stderr)) + self.assertIn("INFO: It Works!", _textmode(stderr)) @defer.inlineCallbacks def test_parse_items(self): status, out, stderr = yield self.execute( ['--spider', self.spider_name, '-c', 'parse', self.url('/html')] ) - self.assertIn("""[{}, {'foo': 'bar'}]""", to_native_str(out)) + self.assertIn("""[{}, {'foo': 'bar'}]""", _textmode(out)) @defer.inlineCallbacks def test_parse_items_no_callback_passed(self): status, out, stderr = yield self.execute( ['--spider', self.spider_name, self.url('/html')] ) - self.assertIn("""[{}, {'foo': 'bar'}]""", to_native_str(out)) + self.assertIn("""[{}, {'foo': 'bar'}]""", _textmode(out)) @defer.inlineCallbacks def test_wrong_callback_passed(self): status, out, stderr = yield self.execute( ['--spider', self.spider_name, '-c', 'dummy', self.url('/html')] ) - self.assertRegexpMatches(to_native_str(out), """# Scraped Items -+\n\[\]""") - self.assertIn("""Cannot find callback""", to_native_str(stderr)) + self.assertRegexpMatches(_textmode(out), """# Scraped Items -+\n\[\]""") + self.assertIn("""Cannot find callback""", _textmode(stderr)) @defer.inlineCallbacks def test_crawlspider_matching_rule_callback_set(self): @@ -159,7 +165,7 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} status, out, stderr = yield self.execute( ['--spider', 'goodcrawl'+self.spider_name, '-r', self.url('/html')] ) - self.assertIn("""[{}, {'foo': 'bar'}]""", to_native_str(out)) + self.assertIn("""[{}, {'foo': 'bar'}]""", _textmode(out)) @defer.inlineCallbacks def test_crawlspider_matching_rule_default_callback(self): @@ -167,7 +173,7 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} status, out, stderr = yield self.execute( ['--spider', 'goodcrawl'+self.spider_name, '-r', self.url('/text')] ) - self.assertIn("""[{}, {'nomatch': 'default'}]""", to_native_str(out)) + self.assertIn("""[{}, {'nomatch': 'default'}]""", _textmode(out)) @defer.inlineCallbacks def test_spider_with_no_rules_attribute(self): @@ -175,15 +181,15 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} status, out, stderr = yield self.execute( ['--spider', self.spider_name, '-r', self.url('/html')] ) - self.assertRegexpMatches(to_native_str(out), """# Scraped Items -+\n\[\]""") - self.assertIn("""No CrawlSpider rules found""", to_native_str(stderr)) + self.assertRegexpMatches(_textmode(out), """# Scraped Items -+\n\[\]""") + self.assertIn("""No CrawlSpider rules found""", _textmode(stderr)) @defer.inlineCallbacks def test_crawlspider_missing_callback(self): status, out, stderr = yield self.execute( ['--spider', 'badcrawl'+self.spider_name, '-r', self.url('/html')] ) - self.assertRegexpMatches(to_native_str(out), """# Scraped Items -+\n\[\]""") + self.assertRegexpMatches(_textmode(out), """# Scraped Items -+\n\[\]""") @defer.inlineCallbacks def test_crawlspider_no_matching_rule(self): @@ -191,5 +197,5 @@ ITEM_PIPELINES = {'%s.pipelines.MyPipeline': 1} status, out, stderr = yield self.execute( ['--spider', 'badcrawl'+self.spider_name, '-r', self.url('/enc-gb18030')] ) - self.assertRegexpMatches(to_native_str(out), """# Scraped Items -+\n\[\]""") - self.assertIn("""Cannot find a rule that matches""", to_native_str(stderr)) + self.assertRegexpMatches(_textmode(out), """# Scraped Items -+\n\[\]""") + self.assertIn("""Cannot find a rule that matches""", _textmode(stderr)) From e7fe243c3e6f71660620441ec6a8ca4605d4333b Mon Sep 17 00:00:00 2001 From: Daniel Grana Date: Wed, 15 Aug 2018 01:09:23 -0700 Subject: [PATCH 39/79] Fix test_crawler under windows --- tests/test_crawler.py | 46 ++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/test_crawler.py b/tests/test_crawler.py index 6a8e11363..0aeb12e58 100644 --- a/tests/test_crawler.py +++ b/tests/test_crawler.py @@ -1,10 +1,9 @@ import logging import tempfile import warnings -import unittest from twisted.internet import defer -import twisted.trial.unittest +from twisted.trial import unittest import scrapy from scrapy.crawler import Crawler, CrawlerRunner, CrawlerProcess @@ -94,26 +93,29 @@ class CrawlerLoggingTestCase(unittest.TestCase): assert get_scrapy_root_handler() is None def test_spider_custom_settings_log_level(self): - with tempfile.NamedTemporaryFile() as log_file: - class MySpider(scrapy.Spider): - name = 'spider' - custom_settings = { - 'LOG_LEVEL': 'INFO', - 'LOG_FILE': log_file.name, - # disable telnet if not available to avoid an extra warning - 'TELNETCONSOLE_ENABLED': telnet.TWISTED_CONCH_AVAILABLE, - } + log_file = self.mktemp() + class MySpider(scrapy.Spider): + name = 'spider' + custom_settings = { + 'LOG_LEVEL': 'INFO', + 'LOG_FILE': log_file, + # disable telnet if not available to avoid an extra warning + 'TELNETCONSOLE_ENABLED': telnet.TWISTED_CONCH_AVAILABLE, + } + + configure_logging() + self.assertEqual(get_scrapy_root_handler().level, logging.DEBUG) + crawler = Crawler(MySpider, {}) + self.assertEqual(get_scrapy_root_handler().level, logging.INFO) + info_count = crawler.stats.get_value('log_count/INFO') + logging.debug('debug message') + logging.info('info message') + logging.warning('warning message') + logging.error('error message') + + with open(log_file, 'rb') as fo: + logged = fo.read().decode('utf8') - configure_logging() - self.assertEqual(get_scrapy_root_handler().level, logging.DEBUG) - crawler = Crawler(MySpider, {}) - self.assertEqual(get_scrapy_root_handler().level, logging.INFO) - info_count = crawler.stats.get_value('log_count/INFO') - logging.debug('debug message') - logging.info('info message') - logging.warning('warning message') - logging.error('error message') - logged = log_file.read().decode('utf8') self.assertNotIn('debug message', logged) self.assertIn('info message', logged) self.assertIn('warning message', logged) @@ -203,7 +205,7 @@ class NoRequestsSpider(scrapy.Spider): return [] -class CrawlerRunnerHasSpider(twisted.trial.unittest.TestCase): +class CrawlerRunnerHasSpider(unittest.TestCase): @defer.inlineCallbacks def test_crawler_runner_bootstrap_successful(self): From a304d6b692664f06c67fb23eb08fd985eda72e21 Mon Sep 17 00:00:00 2001 From: Daniel Grana Date: Wed, 15 Aug 2018 02:02:20 -0700 Subject: [PATCH 40/79] Workaround to pass tests/test_feedexporter.py under windows --- tests/test_feedexport.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_feedexport.py b/tests/test_feedexport.py index 6eefa14bf..76452d450 100644 --- a/tests/test_feedexport.py +++ b/tests/test_feedexport.py @@ -241,10 +241,18 @@ class FeedExportTest(unittest.TestCase): yield runner.crawl(spider_cls) with open(res_path, 'rb') as f: - defer.returnValue(f.read()) + content = f.read() finally: - shutil.rmtree(tmpdir) + # FIXME: Windows fails to remove the file because FeedExporter + # keeps a reference to the temporal file even after + # the spider finished. + try: + shutil.rmtree(tmpdir) + except OSError: + pass + + defer.returnValue(content) @defer.inlineCallbacks def exported_data(self, items, settings): From 4eaf8690b14527d8d74ddd8103810de7c4dcdee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Wed, 15 Aug 2018 08:54:18 -0300 Subject: [PATCH 41/79] Twisted's unittest.Testcase assertRaiess can't be used as context manager --- tests/test_crawler.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_crawler.py b/tests/test_crawler.py index 0aeb12e58..268948a70 100644 --- a/tests/test_crawler.py +++ b/tests/test_crawler.py @@ -143,9 +143,8 @@ class CrawlerRunnerTestCase(BaseCrawlerTest): settings = Settings({ 'SPIDER_LOADER_CLASS': 'tests.test_crawler.SpiderLoaderWithWrongInterface' }) - with warnings.catch_warnings(record=True) as w, \ - self.assertRaises(AttributeError): - CrawlerRunner(settings) + with warnings.catch_warnings(record=True) as w: + self.assertRaises(AttributeError, CrawlerRunner, settings) self.assertEqual(len(w), 1) self.assertIn("SPIDER_LOADER_CLASS", str(w[0].message)) self.assertIn("scrapy.interfaces.ISpiderLoader", str(w[0].message)) From 38608bc2495189bd7de2dc48c0036df217138377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Wed, 15 Aug 2018 11:59:09 -0300 Subject: [PATCH 42/79] Use ignore_errors option from rmtree --- tests/test_feedexport.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/test_feedexport.py b/tests/test_feedexport.py index 76452d450..e46c8c14e 100644 --- a/tests/test_feedexport.py +++ b/tests/test_feedexport.py @@ -244,13 +244,7 @@ class FeedExportTest(unittest.TestCase): content = f.read() finally: - # FIXME: Windows fails to remove the file because FeedExporter - # keeps a reference to the temporal file even after - # the spider finished. - try: - shutil.rmtree(tmpdir) - except OSError: - pass + shutil.rmtree(tmpdir, ignore_errors=True) defer.returnValue(content) From 4de493efdd80d8ff85a78009dcc120ac46b9c55c Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Wed, 15 Aug 2018 20:24:00 +0300 Subject: [PATCH 43/79] Add test_same_url --- tests/test_contracts.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index b07cbee1e..e4f1a777b 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -1,8 +1,10 @@ from unittest import TextTestResult +from twisted.internet import defer from twisted.python import failure from twisted.trial import unittest +from scrapy.crawler import CrawlerRunner from scrapy.spidermiddlewares.httperror import HttpError from scrapy.spiders import Spider from scrapy.http import Request @@ -101,6 +103,29 @@ class TestSpider(Spider): pass +class TestSameUrlSpider(Spider): + + name = 'test_same_url' + + def __init__(self, *args, **kwargs): + super(TestSameUrlSpider, self).__init__(*args, **kwargs) + self.visited = 0 + + def parse_first(self, response): + """first callback + @url http://scrapy.org + """ + self.visited += 1 + return TestItem() + + def parse_second(self, response): + """second callback + @url http://scrapy.org + """ + self.visited += 1 + return TestItem() + + class ContractsManagerTest(unittest.TestCase): contracts = [UrlContract, ReturnsContract, ScrapesContract] @@ -177,14 +202,12 @@ class ContractsManagerTest(unittest.TestCase): self.should_succeed() # scrapes_item_fail - request = self.conman.from_method(spider.scrapes_item_fail, - self.results) + request = self.conman.from_method(spider.scrapes_item_fail, self.results) request.callback(response) self.should_fail() # scrapes_dict_item_fail - request = self.conman.from_method(spider.scrapes_dict_item_fail, - self.results) + request = self.conman.from_method(spider.scrapes_dict_item_fail, self.results) request.callback(response) self.should_fail() @@ -202,3 +225,12 @@ class ContractsManagerTest(unittest.TestCase): self.assertFalse(self.results.failures) self.assertTrue(self.results.errors) + + @defer.inlineCallbacks + def test_same_url(self): + TestSameUrlSpider.start_requests = lambda s: self.conman.from_spider(s, self.results) + + crawler = CrawlerRunner().create_crawler(TestSameUrlSpider) + yield crawler.crawl() + + self.assertEqual(crawler.spider.visited, 2) From 2cb4decb6ad52548b3441c877235c540cef12082 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Wed, 15 Aug 2018 20:36:10 +0300 Subject: [PATCH 44/79] Move TestSameUrlSpider to test method --- tests/test_contracts.py | 45 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index e4f1a777b..430d89253 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -103,29 +103,6 @@ class TestSpider(Spider): pass -class TestSameUrlSpider(Spider): - - name = 'test_same_url' - - def __init__(self, *args, **kwargs): - super(TestSameUrlSpider, self).__init__(*args, **kwargs) - self.visited = 0 - - def parse_first(self, response): - """first callback - @url http://scrapy.org - """ - self.visited += 1 - return TestItem() - - def parse_second(self, response): - """second callback - @url http://scrapy.org - """ - self.visited += 1 - return TestItem() - - class ContractsManagerTest(unittest.TestCase): contracts = [UrlContract, ReturnsContract, ScrapesContract] @@ -228,6 +205,28 @@ class ContractsManagerTest(unittest.TestCase): @defer.inlineCallbacks def test_same_url(self): + + class TestSameUrlSpider(Spider): + name = 'test_same_url' + + def __init__(self, *args, **kwargs): + super(TestSameUrlSpider, self).__init__(*args, **kwargs) + self.visited = 0 + + def parse_first(self, response): + """first callback + @url http://scrapy.org + """ + self.visited += 1 + return TestItem() + + def parse_second(self, response): + """second callback + @url http://scrapy.org + """ + self.visited += 1 + return TestItem() + TestSameUrlSpider.start_requests = lambda s: self.conman.from_spider(s, self.results) crawler = CrawlerRunner().create_crawler(TestSameUrlSpider) From ddd69f4c10578975658ef6ac450cbe1bed85df80 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Wed, 15 Aug 2018 20:39:43 +0300 Subject: [PATCH 45/79] Use MockServer in test_same_url --- tests/test_contracts.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index 430d89253..223a926f1 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -15,6 +15,7 @@ from scrapy.contracts.default import ( ReturnsContract, ScrapesContract, ) +from tests.mockserver import MockServer class TestItem(Item): @@ -213,6 +214,9 @@ class ContractsManagerTest(unittest.TestCase): super(TestSameUrlSpider, self).__init__(*args, **kwargs) self.visited = 0 + def start_requests(s): + return self.conman.from_spider(s, self.results) + def parse_first(self, response): """first callback @url http://scrapy.org @@ -227,9 +231,8 @@ class ContractsManagerTest(unittest.TestCase): self.visited += 1 return TestItem() - TestSameUrlSpider.start_requests = lambda s: self.conman.from_spider(s, self.results) - crawler = CrawlerRunner().create_crawler(TestSameUrlSpider) - yield crawler.crawl() + with MockServer() as mockserver: + yield crawler.crawl(mockserver=mockserver) self.assertEqual(crawler.spider.visited, 2) From 2b212d426668c02704bc23c4e988fa108e0738ef Mon Sep 17 00:00:00 2001 From: Vostretsov Nikita Date: Fri, 17 Aug 2018 14:39:06 +0000 Subject: [PATCH 46/79] ignore cache for pytests --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 406146e5f..ff6e2ea65 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ dist .idea htmlcov/ .coverage +.pytest_cache/ .coverage.* .cache/ From d95762db7c9abe59d8edfc8c17397a5a5bbfc661 Mon Sep 17 00:00:00 2001 From: Vostretsov Nikita Date: Fri, 17 Aug 2018 14:39:24 +0000 Subject: [PATCH 47/79] new signal --- scrapy/signals.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scrapy/signals.py b/scrapy/signals.py index e36c27203..c0e4bb74e 100644 --- a/scrapy/signals.py +++ b/scrapy/signals.py @@ -13,6 +13,7 @@ spider_closed = object() spider_error = object() request_scheduled = object() request_dropped = object() +request_reached_downloader = object() response_received = object() response_downloaded = object() item_scraped = object() From 597b8a97ad468123432dba3d6f2c3dd943f4aa36 Mon Sep 17 00:00:00 2001 From: Vostretsov Nikita Date: Fri, 17 Aug 2018 14:39:42 +0000 Subject: [PATCH 48/79] documentation for new signal --- docs/topics/signals.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/topics/signals.rst b/docs/topics/signals.rst index d40c0e1df..cf7b8db2f 100644 --- a/docs/topics/signals.rst +++ b/docs/topics/signals.rst @@ -279,6 +279,22 @@ request_dropped :param spider: the spider that yielded the request :type spider: :class:`~scrapy.spiders.Spider` object +request_reached_downloader +--------------------------- + +.. signal:: request_reached_downloader +.. function:: request_reached_downloader(request, spider) + + Sent when a :class:`~scrapy.http.Request`, reached downloader. + + The signal does not support returning deferreds from their handlers. + + :param request: the request that reached downloader + :type request: :class:`~scrapy.http.Request` object + + :param spider: the spider that yielded the request + :type spider: :class:`~scrapy.spiders.Spider` object + response_received ----------------- From afb1458bd3b75fad78f053ac062910052971cdfc Mon Sep 17 00:00:00 2001 From: Vostretsov Nikita Date: Fri, 17 Aug 2018 14:39:54 +0000 Subject: [PATCH 49/79] tests for new signal --- tests/test_engine.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_engine.py b/tests/test_engine.py index 719c0c60c..856465161 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -103,6 +103,7 @@ class CrawlerRun(object): self.respplug = [] self.reqplug = [] self.reqdropped = [] + self.reqreached = [] self.itemerror = [] self.itemresp = [] self.signals_catched = {} @@ -124,6 +125,7 @@ class CrawlerRun(object): self.crawler.signals.connect(self.item_error, signals.item_error) self.crawler.signals.connect(self.request_scheduled, signals.request_scheduled) self.crawler.signals.connect(self.request_dropped, signals.request_dropped) + self.crawler.signals.connect(self.request_reached, signals.request_reached_downloader) self.crawler.signals.connect(self.response_downloaded, signals.response_downloaded) self.crawler.crawl(start_urls=start_urls) self.spider = self.crawler.spider @@ -155,6 +157,9 @@ class CrawlerRun(object): def request_scheduled(self, request, spider): self.reqplug.append((request, spider)) + def request_reached(self, request, spider): + self.reqreached.append((request, spider)) + def request_dropped(self, request, spider): self.reqdropped.append((request, spider)) @@ -212,6 +217,8 @@ class EngineTest(unittest.TestCase): responses_count = len(self.run.respplug) self.assertEqual(scheduled_requests_count, dropped_requests_count + responses_count) + self.assertEqual(len(self.run.reqreached), + responses_count) def _assert_dropped_requests(self): self.assertEqual(len(self.run.reqdropped), 1) @@ -219,6 +226,7 @@ class EngineTest(unittest.TestCase): def _assert_downloaded_responses(self): # response tests self.assertEqual(8, len(self.run.respplug)) + self.assertEqual(8, len(self.run.reqreached)) for response, _ in self.run.respplug: if self.run.getpath(response.url) == '/item999.html': From 561ad3b63c539b560bbaed1df37d0b60043ab3b3 Mon Sep 17 00:00:00 2001 From: Vostretsov Nikita Date: Fri, 17 Aug 2018 14:40:24 +0000 Subject: [PATCH 50/79] emit new signal --- scrapy/core/downloader/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scrapy/core/downloader/__init__.py b/scrapy/core/downloader/__init__.py index d835e65f7..59c3ad074 100644 --- a/scrapy/core/downloader/__init__.py +++ b/scrapy/core/downloader/__init__.py @@ -129,6 +129,9 @@ class Downloader(object): return response slot.active.add(request) + self.signals.send_catch_log(signal=signals.request_reached_downloader, + request=request, + spider=spider) deferred = defer.Deferred().addBoth(_deactivate) slot.queue.append((request, deferred)) self._process_queue(spider, slot) From e2de0a7203b99d8fc71e62539fdd75dad439982a Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sat, 18 Aug 2018 15:24:30 +0300 Subject: [PATCH 51/79] Use except Exception --- scrapy/contracts/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py index c62df5ab0..18c59ff22 100644 --- a/scrapy/contracts/__init__.py +++ b/scrapy/contracts/__init__.py @@ -44,7 +44,7 @@ class ContractsManager(object): bound_method = spider.__getattribute__(method) try: requests.append(self.from_method(bound_method, results)) - except: + except Exception: case = _create_testcase(bound_method, 'contract') results.addError(case, sys.exc_info()) From 0467737cf0bc4c5603bd63a87e220482eb724600 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sat, 18 Aug 2018 15:42:21 +0300 Subject: [PATCH 52/79] Fix mockserver usage --- tests/test_contracts.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index c5820f256..0a216b745 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -222,22 +222,20 @@ class ContractsManagerTest(unittest.TestCase): return self.conman.from_spider(s, self.results) def parse_first(self, response): - """first callback - @url {} - """.format(self.mockserver.url('/status?n=200')) self.visited += 1 return TestItem() def parse_second(self, response): - """second callback - @url {} - """.format(self.mockserver.url('/status?n=200')) self.visited += 1 return TestItem() - crawler = CrawlerRunner().create_crawler(TestSameUrlSpider) with MockServer() as mockserver: - yield crawler.crawl(mockserver=mockserver) + mock_endpoint = mockserver.url('/status?n=200') + TestSameUrlSpider.parse_first.__func__.__doc__ = '@url {}'.format(mock_endpoint) + TestSameUrlSpider.parse_second.__func__.__doc__ = '@url {}'.format(mock_endpoint) + + crawler = CrawlerRunner().create_crawler(TestSameUrlSpider) + yield crawler.crawl() self.assertEqual(crawler.spider.visited, 2) From 57824600a8295f0d8537de2a8d1c3eb9977ed36d Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Sun, 19 Aug 2018 16:56:41 +0300 Subject: [PATCH 53/79] Use six.get_unbound_function in test_same_url --- tests/test_contracts.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index 0a216b745..b4209e1f6 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -1,5 +1,6 @@ from unittest import TextTestResult +from six import get_unbound_function from twisted.internet import defer from twisted.python import failure from twisted.trial import unittest @@ -230,9 +231,10 @@ class ContractsManagerTest(unittest.TestCase): return TestItem() with MockServer() as mockserver: - mock_endpoint = mockserver.url('/status?n=200') - TestSameUrlSpider.parse_first.__func__.__doc__ = '@url {}'.format(mock_endpoint) - TestSameUrlSpider.parse_second.__func__.__doc__ = '@url {}'.format(mock_endpoint) + contract_doc = '@url {}'.format(mockserver.url('/status?n=200')) + + get_unbound_function(TestSameUrlSpider.parse_first).__doc__ = contract_doc + get_unbound_function(TestSameUrlSpider.parse_second).__doc__ = contract_doc crawler = CrawlerRunner().create_crawler(TestSameUrlSpider) yield crawler.crawl() From af555cab23958501f07f8b2771c082ac3f3a5961 Mon Sep 17 00:00:00 2001 From: Raphael Wuillemier Date: Wed, 22 Aug 2018 14:15:53 +0200 Subject: [PATCH 54/79] Added general guide for developer tools instead of Firefox and Firebug-sections --- docs/index.rst | 10 +- docs/intro/tutorial.rst | 3 +- docs/topics/_images/inspector_01.png | Bin 0 -> 53922 bytes docs/topics/_images/network_01.png | Bin 0 -> 10720 bytes docs/topics/_images/network_02.png | Bin 0 -> 82702 bytes docs/topics/_images/network_03.png | Bin 0 -> 45506 bytes docs/topics/developer-tools.rst | 248 +++++++++++++++++++++++++++ docs/topics/firebug.rst | 167 ------------------ docs/topics/firefox.rst | 82 --------- 9 files changed, 252 insertions(+), 258 deletions(-) create mode 100644 docs/topics/_images/inspector_01.png create mode 100644 docs/topics/_images/network_01.png create mode 100644 docs/topics/_images/network_02.png create mode 100644 docs/topics/_images/network_03.png create mode 100644 docs/topics/developer-tools.rst delete mode 100644 docs/topics/firebug.rst delete mode 100644 docs/topics/firefox.rst diff --git a/docs/index.rst b/docs/index.rst index 7e8c979c4..0a96aa88e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -151,8 +151,7 @@ Solving specific problems topics/contracts topics/practices topics/broad-crawls - topics/firefox - topics/firebug + topics/developer-tools topics/leaks topics/media-pipeline topics/deploy @@ -175,11 +174,8 @@ Solving specific problems :doc:`topics/broad-crawls` Tune Scrapy for crawling a lot domains in parallel. -:doc:`topics/firefox` - Learn how to scrape with Firefox and some useful add-ons. - -:doc:`topics/firebug` - Learn how to scrape efficiently using Firebug. +:doc:`topics/developer-tools` + Learn how to scrape with your browser's developer tools. :doc:`topics/leaks` Learn how to find and get rid of memory leaks in your crawler. diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index 0db6a6218..fa6dc274d 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -298,8 +298,7 @@ expressions`:: In order to find the proper CSS selectors to use, you might find useful opening the response page from the shell in your web browser using ``view(response)``. -You can use your browser developer tools or extensions like Firebug (see -sections about :ref:`topics-firebug` and :ref:`topics-firefox`). +You can use your browser developer tools (see section about :ref:`topics-developer-tools`). `Selector Gadget`_ is also a nice tool to quickly find CSS selector for visually selected elements, which works in many browsers. diff --git a/docs/topics/_images/inspector_01.png b/docs/topics/_images/inspector_01.png new file mode 100644 index 0000000000000000000000000000000000000000..edb8795dcb8890bac8311db1445435f969761aa0 GIT binary patch literal 53922 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 literal 0 HcmV?d00001 diff --git a/docs/topics/_images/network_01.png b/docs/topics/_images/network_01.png new file mode 100644 index 0000000000000000000000000000000000000000..1788ea76a884912208ea2d41cade5866e0b4ab9d GIT binary patch literal 10720 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 R-{W(ysjl<5?9sDV{|1*77K;D? literal 0 HcmV?d00001 diff --git a/docs/topics/_images/network_02.png b/docs/topics/_images/network_02.png new file mode 100644 index 0000000000000000000000000000000000000000..5d39ae601ae7c29c8f69fa4709859e0a2006e54f GIT binary patch literal 82702 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 Tyui452mZ)PDoT`#8T$P{6=#Uk literal 0 HcmV?d00001 diff --git a/docs/topics/developer-tools.rst b/docs/topics/developer-tools.rst new file mode 100644 index 000000000..dd1dd3a62 --- /dev/null +++ b/docs/topics/developer-tools.rst @@ -0,0 +1,248 @@ +.. _topics-developer-tools: + +========================== +Using your browser's Developer Tools for scraping +========================== + +Here is a general guide on how to use your browser's Developer Tools +to ease the scraping process. Today almost all browsers come with +built in `Developer Tools`_ and although we will use Firefox in this +guide, the concepts are applicable to any other browser. + +In this guide we'll introduce the basic tools to use from a browser's +Developer Tools by scraping `quotes.toscrape.com`_. + +.. _topics-livedom: + +Caveats with inspecting the live browser DOM +============================================ + +Since Developer Tools operate on a live browser DOM, what you'll actually see +when inspecting the page source is not the original HTML, but a modified one +after applying some browser clean up and executing Javascript code. Firefox, +in particular, is known for adding ```` elements to tables. Scrapy, on +the other hand, does not modify the original page HTML, so you won't be able to +extract any data if you use ```` in your XPath expressions. + +Therefore, you should keep in mind the following things: + +* Disable Javascript while inspecting the DOM looking for XPaths to be + used in Scrapy + +* Never use full XPath paths, use relative and clever ones based on attributes + (such as ``id``, ``class``, ``width``, etc) or any identifying features like + ``contains(@href, 'image')``. + +* Never include ```` elements in your XPath expressions unless you + really know what you're doing + +.. _topics-inspector: + +Inspecting a website +=================================== + +By far the most handy feature of the Developer Tools is the `Inspector` +feature, which allows you to inspect the underlying HTML code of +any webpage. To demonstrate the Inspector, let's take a +look at the `quotes.toscrape.com`_-site. + +On the site we have a total of ten quotes from various authors with specific +tags, as well as the Top Ten Tags. Let's say we want to extract all the quotes +on this page, without any meta-information about authors, tags, etc. + +Instead of viewing the whole source code for the page, we can simply right click +on a quote and select ``Inspect Element (Q)``, which opens up the `Inspector`. +In it you should see something like this: + +.. image:: _images/inspector_01.png + :width: 777 + :height: 469 + :alt: Firefox's Inspector-tool + +The interesting part for us is this: + +.. code-block:: html + +
+ (...) + (...) +
(...)
+
+ +If you hover over the first ``div`` directly above the ``span``-tag highlighted +in the screenshot, you'll see that the corresponding section of the webpage gets +highlighted as well. So now we have a section, but we can't find our quote text +anywhere. + +The advantage of the `Inspector` is that it automatically expands and collapses +sections and tags of a webpage, which greatly improves readability. You can +expand and collapse a tag by clicking on the arrow in front of it or by double +clicking directly on the tag. If we expand the ``span``-tag with the ``class= +"text"`` we will see the quote-text we clicked on. The `Inspector` lets you +copy XPaths to selected elements. Let's try it out: Right-click on the ``span``- +tag, select ``Copy > XPath`` and paste it in the scrapy shell like so:: + + >>> scrapy shell "http://quotes.toscrape.com/" + (...) + >>> response.xpath('/html/body/div/div[2]/div[1]/div[1]/span[1]/text()').extract() + ['"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”] + +Adding ``text()`` at the end we are able to extract the first quote with this +basic selector. But this XPath is not really that clever. All it does is +go down a desired path in the source code starting from ``html``. So let's +see if we can refine our XPath a bit: + +If we check the `Inspector` again we'll see that directly beneath our +expanded ``div``-tag we have eight identical ``div``-tags, each with the +same attributes as our first. If we expand any of them, we'll see the same +structure as with our first quote: Two ``span``-tags and one ``div``-tag. We can +expand each ``span``-tag with the ``class="text"`` inside our ``div``-tags and +see each quote. With this knowledge we can refine our XPath: Instead of a path +to follow, we'll simply select all ``span``-tags with the ``class="text"``:: + + >>> response.xpath('//span[@class="text"]/text()').extract() + ['"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”, + '“It is our choices, Harry, that show what we truly are, far more than our abilities.”', + '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”', + (...)] + +And with one simple, cleverer XPath we are able to extract all quotes from +the page. We could have constructed a loop over our first XPath to increase +the number of the last ``div``, but this would have been unnecessarily +complex and by simply constructing an XPath with the ``class="text"`` we +were able to extract all quotes in one line. + +The `Inspector` has a lot of other helpful features, such as searching in the +source code or directly scrolling to an element you selected. Let's demonstrate +a use case: + +Say you want to find the ``Next``-button on the page. Type ``Next`` into the +search bar on the top right of the `Inspector`. You should get two results. +The first is a ``li``-tag with the ``class="text"``, the second the text +of an ``a``-tag. Right click on the ``a``-tag and select ``Scroll into View``. +If you hover over the tag, you'll see the button highlighted. From here +we could easily create a :ref:`Link Extract ` to +follow the pagination. On a simple site such as this, there may not be +the need to find an element visually but the ``Scroll into View`` function +can be quite useful on complex sites. + +.. _topics-network-tool: + +The Network-tool +================ +While scraping you may come across dynamic webpages where some parts +of the page is loaded dynamically through multiple requests. While +this can be quite tricky, the `Network`-tool in the Developer Tools +greatly facilitates this task. To demonstrate the Network-tool, let's +take a look at the page `quotes.toscrape.com/scroll`_. + +The page is quite similar to the basic `quotes.toscrape.com`_-page, +but instead of the above-mentioned ``Next``-button, the page +automatically loads new quotes when you scroll to the bottom. We +could go ahead and try out different XPaths directly, but instead +we'll check another quite useful command from the scrapy shell:: + + >>> scrapy shell "quotes.toscrape.com/scroll" + (...) + >>> view(response) + +A browser window should open with the webpage but with one +crucial difference: Instead of the quotes we just see a greenish +bar with the word ``Loading...``. + +.. image:: _images/network_01.png + :width: 777 + :height: 296 + :alt: Response from quotes.toscrape.com/scroll + +The ``view(response)``-command let's us view the response our +shell or later our spider receives from the server. Here we see +that some basic template is loaded which includes the title, +the login-button and the footer, but the quotes are missing. This +tells us that the quotes are being loaded from a different request +than ``quotes.toscrape/scroll``. + +If you click on the ``Network``-tab, you will probably only see +two entries. The first thing we do is enable persistent logs by +clicking on ``Persist Logs``. If this option is disabled, the +log is automatically cleared each time you navigate to a different +page. Enabling this option is a good default, since it gives us +control on when to clear the logs. + +If we reload the page now, you'll see the log get populated with six +new requests. + +.. image:: _images/network_02.png + :width: 777 + :height: 241 + :alt: Network tab with persistent logs and requests + +Here we see every request that has been made when reloading the page +and can inspect each request and its response. So let's find out +where our quotes are coming from: + +First click on the request with the name ``scroll``. On the right +you can now inspect the request. In ``Headers`` you'll find details +about the request headers, such as the URL, the method, the IP-address, +and so on. We'll ignore the other tabs and click directly on ``Reponse``. + +What you should see in the ``Preview``-pane is the rendered HTML-code, +that is exactly what we saw when we called ``view(response`` in the +shell. Accordingly the ``type`` of the request in the log is ``html``. +The other requests have types like ``css`` or ``js``, but what +interests us is the one request called ``quotes?page=1`` with the +type ``json``. + +If we click on this request, we see that the request URL is +``http://quotes.toscrape.com/api/quotes?page=1`` and the response +is a JSON-object that contains our quotes. We can also right-click +on the request and open ``Open in new tab`` to get a better overview. + +.. image:: _images/network_03.png + :width: 777 + :height: 375 + :alt: JSON-object returned from the quotes.toscrape API + +With this response we can now easily parse the JSON-object and +also request each page to get every quote on the site:: + + import scrapy + import json + + + class QuoteSpider(scrapy.Spider): + name = 'quote' + allowed_domains = ['quotes.toscrape.com'] + page = 1 + start_urls = ['http://quotes.toscrape.com/api/quotes?page=1] + + def parse(self, response): + data = json.loads(response.text) + for quote in data["quotes"]: + quote = quote["text"] + print(quote) + if data["has_next"]: + self.page += 1 + url = "http://quotes.toscrape.com/api/quotes?page={}".format(self.page) + yield scrapy.Request(url=url, callback=self.parse) + +This spider starts at the first page of the quotes-API. With each +response, we parse the ``response.text`` and assign it to ``data``. +This lets us operate on the JSON-object like on a Python dictionary. +We iterate through the ``quotes`` and print out the ``quote["text"]``. +If the handy ``has_next``-element is ``true`` (try loading +`http://quotes.toscrape.com/api/quotes?page=10`_ in your browser or a +page-number greater than 10), we increment the ``page``-attribute +and ``yield`` a new request, inserting the incremented page-number +into our ``url``. + +You can see that with a few inspections in the `Network`-tool we +were able to easily replicate the dynamic requests of the scrolling +functionality of the page. Crawling dynamic pages can be quite +daunting and pages can be very complex, but it (mostly) boils down +to identifying the correct request and replicating it in your spider. + +.. _Developer Tools: https://en.wikipedia.org/wiki/Web_development_tools +.. _quotes.toscrape.com: http://quotes.toscrape.com +.. _quotes.toscrape.com/scroll: quotes.toscrape.com/scroll/ + diff --git a/docs/topics/firebug.rst b/docs/topics/firebug.rst deleted file mode 100644 index 4ea8d3bd0..000000000 --- a/docs/topics/firebug.rst +++ /dev/null @@ -1,167 +0,0 @@ -.. _topics-firebug: - -========================== -Using Firebug for scraping -========================== - -.. note:: Google Directory, the example website used in this guide is no longer - available as it `has been shut down by Google`_. The concepts in this guide - are still valid though. If you want to update this guide to use a new - (working) site, your contribution will be more than welcome!. See :ref:`topics-contributing` - for information on how to do so. - -Introduction -============ - -This document explains how to use `Firebug`_ (a Firefox add-on) to make the -scraping process easier and more fun. For other useful Firefox add-ons see -:ref:`topics-firefox-addons`. There are some caveats with using Firefox add-ons -to inspect pages, see :ref:`topics-firefox-livedom`. - -In this example, we'll show how to use `Firebug`_ to scrape data from the -`Google Directory`_, which contains the same data as the `Open Directory -Project`_ used in the :ref:`tutorial ` but with a different -face. - -.. _Firebug: https://getfirebug.com/ -.. _Google Directory: http://directory.google.com/ -.. _Open Directory Project: http://www.dmoz.org - -Firebug comes with a very useful feature called `Inspect Element`_ which allows -you to inspect the HTML code of the different page elements just by hovering -your mouse over them. Otherwise you would have to search for the tags manually -through the HTML body which can be a very tedious task. - -.. _Inspect Element: https://www.youtube.com/watch?v=-pT_pDe54aA - -In the following screenshot you can see the `Inspect Element`_ tool in action. - -.. image:: _images/firebug1.png - :width: 913 - :height: 600 - :alt: Inspecting elements with Firebug - -At first sight, we can see that the directory is divided in categories, which -are also divided in subcategories. - -However, it seems that there are more subcategories than the ones being shown -in this page, so we'll keep looking: - -.. image:: _images/firebug2.png - :width: 819 - :height: 629 - :alt: Inspecting elements with Firebug - -As expected, the subcategories contain links to other subcategories, and also -links to actual websites, which is the purpose of the directory. - -Getting links to follow -======================= - -By looking at the category URLs we can see they share a pattern: - - http://directory.google.com/Category/Subcategory/Another_Subcategory - -Once we know that, we are able to construct a regular expression to follow -those links. For example, the following one:: - - directory\.google\.com/[A-Z][a-zA-Z_/]+$ - -So, based on that regular expression we can create the first crawling rule:: - - Rule(LinkExtractor(allow='directory.google.com/[A-Z][a-zA-Z_/]+$', ), - 'parse_category', - follow=True, - ), - -The :class:`~scrapy.spiders.Rule` object instructs -:class:`~scrapy.spiders.CrawlSpider` based spiders how to follow the -category links. ``parse_category`` will be a method of the spider which will -process and extract data from those pages. - -This is how the spider would look so far:: - - from scrapy.linkextractors import LinkExtractor - from scrapy.spiders import CrawlSpider, Rule - - class GoogleDirectorySpider(CrawlSpider): - name = 'directory.google.com' - allowed_domains = ['directory.google.com'] - start_urls = ['http://directory.google.com/'] - - rules = ( - Rule(LinkExtractor(allow='directory\.google\.com/[A-Z][a-zA-Z_/]+$'), - 'parse_category', follow=True, - ), - ) - - def parse_category(self, response): - # write the category page data extraction code here - pass - - -Extracting the data -=================== - -Now we're going to write the code to extract data from those pages. - -With the help of Firebug, we'll take a look at some page containing links to -websites (say http://directory.google.com/Top/Arts/Awards/) and find out how we can -extract those links using :ref:`Selectors `. We'll also -use the :ref:`Scrapy shell ` to test those XPath's and make sure -they work as we expect. - -.. image:: _images/firebug3.png - :width: 965 - :height: 751 - :alt: Inspecting elements with Firebug - -As you can see, the page markup is not very descriptive: the elements don't -contain ``id``, ``class`` or any attribute that clearly identifies them, so -we'll use the ranking bars as a reference point to select the data to extract -when we construct our XPaths. - -After using FireBug, we can see that each link is inside a ``td`` tag, which is -itself inside a ``tr`` tag that also contains the link's ranking bar (in -another ``td``). - -So we can select the ranking bar, then find its parent (the ``tr``), and then -finally, the link's ``td`` (which contains the data we want to scrape). - -This results in the following XPath:: - - //td[descendant::a[contains(@href, "#pagerank")]]/following-sibling::td//a - -It's important to use the :ref:`Scrapy shell ` to test these -complex XPath expressions and make sure they work as expected. - -Basically, that expression will look for the ranking bar's ``td`` element, and -then select any ``td`` element who has a descendant ``a`` element whose -``href`` attribute contains the string ``#pagerank``" - -Of course, this is not the only XPath, and maybe not the simpler one to select -that data. Another approach could be, for example, to find any ``font`` tags -that have that grey colour of the links, - -Finally, we can write our ``parse_category()`` method:: - - def parse_category(self, response): - # The path to website links in directory page - links = response.xpath('//td[descendant::a[contains(@href, "#pagerank")]]/following-sibling::td/font') - - for link in links: - item = DirectoryItem() - item['name'] = link.xpath('a/text()').extract() - item['url'] = link.xpath('a/@href').extract() - item['description'] = link.xpath('font[2]/text()').extract() - yield item - - -Be aware that you may find some elements which appear in Firebug but -not in the original HTML, such as the typical case of ```` -elements. - -or tags which Therefer in page HTML -sources may on Firebug inspects the live DOM - -.. _has been shut down by Google: https://searchenginewatch.com/sew/news/2096661/google-directory-shut diff --git a/docs/topics/firefox.rst b/docs/topics/firefox.rst deleted file mode 100644 index 2c85848be..000000000 --- a/docs/topics/firefox.rst +++ /dev/null @@ -1,82 +0,0 @@ -.. _topics-firefox: - -========================== -Using Firefox for scraping -========================== - -Here is a list of tips and advice on using Firefox for scraping, along with a -list of useful Firefox add-ons to ease the scraping process. - -.. _topics-firefox-livedom: - -Caveats with inspecting the live browser DOM -============================================ - -Since Firefox add-ons operate on a live browser DOM, what you'll actually see -when inspecting the page source is not the original HTML, but a modified one -after applying some browser clean up and executing Javascript code. Firefox, -in particular, is known for adding ```` elements to tables. Scrapy, on -the other hand, does not modify the original page HTML, so you won't be able to -extract any data if you use ```` in your XPath expressions. - -Therefore, you should keep in mind the following things when working with -Firefox and XPath: - -* Disable Firefox Javascript while inspecting the DOM looking for XPaths to be - used in Scrapy - -* Never use full XPath paths, use relative and clever ones based on attributes - (such as ``id``, ``class``, ``width``, etc) or any identifying features like - ``contains(@href, 'image')``. - -* Never include ```` elements in your XPath expressions unless you - really know what you're doing - -.. _topics-firefox-addons: - -Useful Firefox add-ons for scraping -=================================== - -Firebug -------- - -`Firebug`_ is a widely known tool among web developers and it's also very -useful for scraping. In particular, its `Inspect Element`_ feature comes very -handy when you need to construct the XPaths for extracting data because it -allows you to view the HTML code of each page element while moving your mouse -over it. - -See :ref:`topics-firebug` for a detailed guide on how to use Firebug with -Scrapy. - -XPather -------- - -`XPather`_ allows you to test XPath expressions directly on the pages. - -XPath Checker -------------- - -`XPath Checker`_ is another Firefox add-on for testing XPaths on your pages. - -Tamper Data ------------ - -`Tamper Data`_ is a Firefox add-on which allows you to view and modify the HTTP -request headers sent by Firefox. Firebug also allows to view HTTP headers, but -not to modify them. - -Firecookie ----------- - -`Firecookie`_ makes it easier to view and manage cookies. You can use this -extension to create a new cookie, delete existing cookies, see a list of cookies -for the current site, manage cookies permissions and a lot more. - -.. _Firebug: https://getfirebug.com/ -.. _Inspect Element: https://www.youtube.com/watch?v=-pT_pDe54aA -.. _XPather: https://addons.mozilla.org/en-US/firefox/addon/xpather/ -.. _XPath Checker: https://addons.mozilla.org/en-US/firefox/addon/xpath-checker/ -.. _Tamper Data: https://addons.mozilla.org/en-US/firefox/addon/tamper-data/ -.. _Firecookie: https://addons.mozilla.org/en-US/firefox/addon/firecookie/ - From 3a71e7dbce94e6056a5258c59df0150a94ddc187 Mon Sep 17 00:00:00 2001 From: testingcan Date: Wed, 22 Aug 2018 16:57:51 +0200 Subject: [PATCH 55/79] Increased length of "=" --- docs/topics/developer-tools.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/developer-tools.rst b/docs/topics/developer-tools.rst index dd1dd3a62..aa4050948 100644 --- a/docs/topics/developer-tools.rst +++ b/docs/topics/developer-tools.rst @@ -1,8 +1,8 @@ .. _topics-developer-tools: -========================== +================================================= Using your browser's Developer Tools for scraping -========================== +================================================= Here is a general guide on how to use your browser's Developer Tools to ease the scraping process. Today almost all browsers come with From 4d3aaabbcaa3a39ba56f443ccfe8a60888223013 Mon Sep 17 00:00:00 2001 From: Raphael Wuillemier Date: Thu, 23 Aug 2018 12:40:31 +0200 Subject: [PATCH 56/79] Updated code, added code snippets and improved readability --- docs/topics/developer-tools.rst | 84 ++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/docs/topics/developer-tools.rst b/docs/topics/developer-tools.rst index aa4050948..6bd2c052a 100644 --- a/docs/topics/developer-tools.rst +++ b/docs/topics/developer-tools.rst @@ -27,7 +27,7 @@ extract any data if you use ```` in your XPath expressions. Therefore, you should keep in mind the following things: * Disable Javascript while inspecting the DOM looking for XPaths to be - used in Scrapy + used in Scrapy (in the Developer Tools settings click `Disable JavaScript`) * Never use full XPath paths, use relative and clever ones based on attributes (such as ``id``, ``class``, ``width``, etc) or any identifying features like @@ -43,8 +43,8 @@ Inspecting a website By far the most handy feature of the Developer Tools is the `Inspector` feature, which allows you to inspect the underlying HTML code of -any webpage. To demonstrate the Inspector, let's take a -look at the `quotes.toscrape.com`_-site. +any webpage. To demonstrate the Inspector, let's look at the +`quotes.toscrape.com`_-site. On the site we have a total of ten quotes from various authors with specific tags, as well as the Top Ten Tags. Let's say we want to extract all the quotes @@ -69,7 +69,7 @@ The interesting part for us is this:
(...)
-If you hover over the first ``div`` directly above the ``span``-tag highlighted +If you hover over the first ``div`` directly above the ``span`` tag highlighted in the screenshot, you'll see that the corresponding section of the webpage gets highlighted as well. So now we have a section, but we can't find our quote text anywhere. @@ -77,14 +77,14 @@ anywhere. The advantage of the `Inspector` is that it automatically expands and collapses sections and tags of a webpage, which greatly improves readability. You can expand and collapse a tag by clicking on the arrow in front of it or by double -clicking directly on the tag. If we expand the ``span``-tag with the ``class= +clicking directly on the tag. If we expand the ``span`` tag with the ``class= "text"`` we will see the quote-text we clicked on. The `Inspector` lets you -copy XPaths to selected elements. Let's try it out: Right-click on the ``span``- +copy XPaths to selected elements. Let's try it out: Right-click on the ``span`` tag, select ``Copy > XPath`` and paste it in the scrapy shell like so:: - >>> scrapy shell "http://quotes.toscrape.com/" + $ scrapy shell "http://quotes.toscrape.com/" (...) - >>> response.xpath('/html/body/div/div[2]/div[1]/div[1]/span[1]/text()').extract() + >>> response.xpath('/html/body/div/div[2]/div[1]/div[1]/span[1]/text()').getall() ['"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”] Adding ``text()`` at the end we are able to extract the first quote with this @@ -93,14 +93,28 @@ go down a desired path in the source code starting from ``html``. So let's see if we can refine our XPath a bit: If we check the `Inspector` again we'll see that directly beneath our -expanded ``div``-tag we have eight identical ``div``-tags, each with the +expanded ``div`` tag we have nine identical ``div`` tags, each with the same attributes as our first. If we expand any of them, we'll see the same -structure as with our first quote: Two ``span``-tags and one ``div``-tag. We can -expand each ``span``-tag with the ``class="text"`` inside our ``div``-tags and -see each quote. With this knowledge we can refine our XPath: Instead of a path -to follow, we'll simply select all ``span``-tags with the ``class="text"``:: +structure as with our first quote: Two ``span`` tags and one ``div`` tag. We can +expand each ``span`` tag with the ``class="text"`` inside our ``div`` tags and +see each quote: - >>> response.xpath('//span[@class="text"]/text()').extract() +.. code-block:: html + +
+ + “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.” + + (...) +
(...)
+
+ + +With this knowledge we can refine our XPath: Instead of a path to follow, +we'll simply select all ``span`` tags with the ``class="text"`` by using +the `has-class-extension`_:: + + >>> response.xpath('//span[has-class("text")]/text()').getall() ['"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”, '“It is our choices, Harry, that show what we truly are, far more than our abilities.”', '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”', @@ -109,40 +123,45 @@ to follow, we'll simply select all ``span``-tags with the ``class="text"``:: And with one simple, cleverer XPath we are able to extract all quotes from the page. We could have constructed a loop over our first XPath to increase the number of the last ``div``, but this would have been unnecessarily -complex and by simply constructing an XPath with the ``class="text"`` we -were able to extract all quotes in one line. +complex and by simply constructing an XPath with ``has-class("text")`` +we were able to extract all quotes in one line. The `Inspector` has a lot of other helpful features, such as searching in the source code or directly scrolling to an element you selected. Let's demonstrate a use case: -Say you want to find the ``Next``-button on the page. Type ``Next`` into the +Say you want to find the ``Next`` button on the page. Type ``Next`` into the search bar on the top right of the `Inspector`. You should get two results. -The first is a ``li``-tag with the ``class="text"``, the second the text -of an ``a``-tag. Right click on the ``a``-tag and select ``Scroll into View``. +The first is a ``li`` tag with the ``class="text"``, the second the text +of an ``a`` tag. Right click on the ``a`` tag and select ``Scroll into View``. If you hover over the tag, you'll see the button highlighted. From here -we could easily create a :ref:`Link Extract ` to +we could easily create a :ref:`Link Extractor ` to follow the pagination. On a simple site such as this, there may not be the need to find an element visually but the ``Scroll into View`` function can be quite useful on complex sites. +Note that the search bar can also be used to search for and test CSS +selectors. For example, you could search for ``span.text`` to find +all quote texts. Instead of a full text search, this searches for +exactly the ``span`` tag with the ``class="text"`` in the page. + .. _topics-network-tool: The Network-tool ================ While scraping you may come across dynamic webpages where some parts -of the page is loaded dynamically through multiple requests. While +of the page are loaded dynamically through multiple requests. While this can be quite tricky, the `Network`-tool in the Developer Tools greatly facilitates this task. To demonstrate the Network-tool, let's take a look at the page `quotes.toscrape.com/scroll`_. The page is quite similar to the basic `quotes.toscrape.com`_-page, -but instead of the above-mentioned ``Next``-button, the page +but instead of the above-mentioned ``Next`` button, the page automatically loads new quotes when you scroll to the bottom. We could go ahead and try out different XPaths directly, but instead we'll check another quite useful command from the scrapy shell:: - >>> scrapy shell "quotes.toscrape.com/scroll" + $ scrapy shell "quotes.toscrape.com/scroll" (...) >>> view(response) @@ -155,14 +174,14 @@ bar with the word ``Loading...``. :height: 296 :alt: Response from quotes.toscrape.com/scroll -The ``view(response)``-command let's us view the response our +The ``view(response)`` command let's us view the response our shell or later our spider receives from the server. Here we see that some basic template is loaded which includes the title, the login-button and the footer, but the quotes are missing. This tells us that the quotes are being loaded from a different request than ``quotes.toscrape/scroll``. -If you click on the ``Network``-tab, you will probably only see +If you click on the ``Network`` tab, you will probably only see two entries. The first thing we do is enable persistent logs by clicking on ``Persist Logs``. If this option is disabled, the log is automatically cleared each time you navigate to a different @@ -186,8 +205,8 @@ you can now inspect the request. In ``Headers`` you'll find details about the request headers, such as the URL, the method, the IP-address, and so on. We'll ignore the other tabs and click directly on ``Reponse``. -What you should see in the ``Preview``-pane is the rendered HTML-code, -that is exactly what we saw when we called ``view(response`` in the +What you should see in the ``Preview`` pane is the rendered HTML-code, +that is exactly what we saw when we called ``view(response)`` in the shell. Accordingly the ``type`` of the request in the log is ``html``. The other requests have types like ``css`` or ``js``, but what interests us is the one request called ``quotes?page=1`` with the @@ -219,8 +238,7 @@ also request each page to get every quote on the site:: def parse(self, response): data = json.loads(response.text) for quote in data["quotes"]: - quote = quote["text"] - print(quote) + yield {"quote": quote["text"] if data["has_next"]: self.page += 1 url = "http://quotes.toscrape.com/api/quotes?page={}".format(self.page) @@ -230,9 +248,9 @@ This spider starts at the first page of the quotes-API. With each response, we parse the ``response.text`` and assign it to ``data``. This lets us operate on the JSON-object like on a Python dictionary. We iterate through the ``quotes`` and print out the ``quote["text"]``. -If the handy ``has_next``-element is ``true`` (try loading -`http://quotes.toscrape.com/api/quotes?page=10`_ in your browser or a -page-number greater than 10), we increment the ``page``-attribute +If the handy ``has_next`` element is ``true`` (try loading +`quotes.toscrape.com/api/quotes?page=10`_ in your browser or a +page-number greater than 10), we increment the ``page`` attribute and ``yield`` a new request, inserting the incremented page-number into our ``url``. @@ -245,4 +263,6 @@ to identifying the correct request and replicating it in your spider. .. _Developer Tools: https://en.wikipedia.org/wiki/Web_development_tools .. _quotes.toscrape.com: http://quotes.toscrape.com .. _quotes.toscrape.com/scroll: quotes.toscrape.com/scroll/ +.. _quotes.toscrape.com/api/quotes?page=10: http://quotes.toscrape.com/api/quotes?page=10 +.. _has-class-extension: https://parsel.readthedocs.io/en/latest/usage.html#other-xpath-extensions From e98e7f8506b401f80fa193faa741c2094d7b62d7 Mon Sep 17 00:00:00 2001 From: testingcan Date: Thu, 23 Aug 2018 14:50:49 +0200 Subject: [PATCH 57/79] Added missing curly brace --- docs/topics/developer-tools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/developer-tools.rst b/docs/topics/developer-tools.rst index 6bd2c052a..c1976258d 100644 --- a/docs/topics/developer-tools.rst +++ b/docs/topics/developer-tools.rst @@ -238,7 +238,7 @@ also request each page to get every quote on the site:: def parse(self, response): data = json.loads(response.text) for quote in data["quotes"]: - yield {"quote": quote["text"] + yield {"quote": quote["text"]} if data["has_next"]: self.page += 1 url = "http://quotes.toscrape.com/api/quotes?page={}".format(self.page) From 79de3d569a3b50b3a07ced99d8e482862a979e88 Mon Sep 17 00:00:00 2001 From: Raphael Wuillemier Date: Thu, 23 Aug 2018 16:19:13 +0200 Subject: [PATCH 58/79] Removed obsolete firebug-images --- docs/topics/_images/firebug1.png | Bin 44391 -> 0 bytes docs/topics/_images/firebug2.png | Bin 69392 -> 0 bytes docs/topics/_images/firebug3.png | Bin 89644 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/topics/_images/firebug1.png delete mode 100644 docs/topics/_images/firebug2.png delete mode 100644 docs/topics/_images/firebug3.png diff --git a/docs/topics/_images/firebug1.png b/docs/topics/_images/firebug1.png deleted file mode 100644 index e2eaefa838e627c3784ee76c5c8b13dd60783e59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44391 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∈#%JQ4ej)*a756p8k1%){<#Q|wY!P@P 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 X`n*5%RwE@&zab-~AXy-8^ya?+Th!kC diff --git a/docs/topics/_images/firebug2.png b/docs/topics/_images/firebug2.png deleted file mode 100644 index 4cab634311cdc6f7349c70d5f30613484a770009..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69392 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 jl!O{G=T>r4J~Qu^u+32 Date: Fri, 24 Aug 2018 15:18:16 -0400 Subject: [PATCH 59/79] PEP8 ofsite middleware --- scrapy/spidermiddlewares/offsite.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/scrapy/spidermiddlewares/offsite.py b/scrapy/spidermiddlewares/offsite.py index 310166cad..232e96cbb 100644 --- a/scrapy/spidermiddlewares/offsite.py +++ b/scrapy/spidermiddlewares/offsite.py @@ -3,7 +3,6 @@ Offsite Spider Middleware See documentation in docs/topics/spider-middleware.rst """ - import re import logging import warnings @@ -35,8 +34,9 @@ class OffsiteMiddleware(object): domain = urlparse_cached(x).hostname if domain and domain not in self.domains_seen: self.domains_seen.add(domain) - logger.debug("Filtered offsite request to %(domain)r: %(request)s", - {'domain': domain, 'request': x}, extra={'spider': spider}) + logger.debug( + "Filtered offsite request to %(domain)r: %(request)s", + {'domain': domain, 'request': x}, extra={'spider': spider}) self.stats.inc_value('offsite/domains', spider=spider) self.stats.inc_value('offsite/filtered', spider=spider) else: @@ -52,13 +52,15 @@ class OffsiteMiddleware(object): """Override this method to implement a different offsite policy""" allowed_domains = getattr(spider, 'allowed_domains', None) if not allowed_domains: - return re.compile('') # allow all by default + return re.compile('') # allow all by default url_pattern = re.compile("^https?://.*$") for domain in allowed_domains: if url_pattern.match(domain): - warnings.warn("allowed_domains accepts only domains, not URLs. Ignoring URL entry %s in allowed_domains." % domain, URLWarning) - - regex = r'^(.*\.)?(%s)$' % '|'.join(re.escape(d) for d in allowed_domains if d is not None) + message = ("allowed_domains accepts only domains, not URLs. " + "Ignoring URL entry %s in allowed_domains." % domain) + warnings.warn(message, URLWarning) + domains = [re.escape(d) for d in allowed_domains if d is not None] + regex = r'^(.*\.)?(%s)$' % '|'.join(domains) return re.compile(regex) def spider_opened(self, spider): From c02cfa574cc47d6b086cc66025ddef9f3174ac02 Mon Sep 17 00:00:00 2001 From: Vostretsov Nikita Date: Wed, 29 Aug 2018 11:21:55 +0000 Subject: [PATCH 60/79] remove comma --- docs/topics/signals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/signals.rst b/docs/topics/signals.rst index cf7b8db2f..ff07b9d55 100644 --- a/docs/topics/signals.rst +++ b/docs/topics/signals.rst @@ -285,7 +285,7 @@ request_reached_downloader .. signal:: request_reached_downloader .. function:: request_reached_downloader(request, spider) - Sent when a :class:`~scrapy.http.Request`, reached downloader. + Sent when a :class:`~scrapy.http.Request` reached downloader. The signal does not support returning deferreds from their handlers. From 8dbbbd13950dcb21dda759b073c64ffdca85c2d6 Mon Sep 17 00:00:00 2001 From: Stas Glubokiy Date: Mon, 3 Sep 2018 20:07:37 +0300 Subject: [PATCH 61/79] Use request_cls attribute in contract definition --- docs/topics/contracts.rst | 5 +++-- scrapy/contracts/__init__.py | 15 +++++++++------ tests/test_contracts.py | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/topics/contracts.rst b/docs/topics/contracts.rst index ada6fd227..70f20d4ed 100644 --- a/docs/topics/contracts.rst +++ b/docs/topics/contracts.rst @@ -86,8 +86,9 @@ override three methods: .. method:: Contract.adjust_request_args(args) This receives a ``dict`` as an argument containing default arguments - for request object. :class:`~scrapy.http.Request` is used - if ``request_cls`` is not set on ``args``. + for request object. :class:`~scrapy.http.Request` is used by default, + but this can be changed with the ``request_cls`` attribute. + If multiple contracts in chain have this attribute defined, the last one is used. Must return the same or a modified version of it. diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py index 801c18e73..851a26a8e 100644 --- a/scrapy/contracts/__init__.py +++ b/scrapy/contracts/__init__.py @@ -4,7 +4,6 @@ from functools import wraps from inspect import getmembers from unittest import TestCase -from scrapy import FormRequest from scrapy.http import Request from scrapy.utils.spider import iterate_spider_output from scrapy.utils.python import get_spec @@ -50,14 +49,17 @@ class ContractsManager(object): def from_method(self, method, results): contracts = self.extract_contracts(method) if contracts: - # prepare request arguments - kwargs = {'callback': method} + request_cls = Request + for contract in contracts: + if contract.request_cls is not None: + request_cls = contract.request_cls + + # calculate request args + args, kwargs = get_spec(request_cls.__init__) + kwargs['callback'] = method for contract in contracts: kwargs = contract.adjust_request_args(kwargs) - request_cls = kwargs.pop('request_cls', Request) - - args, _ = get_spec(request_cls.__init__) args.remove('self') # check if all positional arguments are defined in kwargs @@ -98,6 +100,7 @@ class ContractsManager(object): class Contract(object): """ Abstract class for contracts """ + request_cls = None def __init__(self, method, *args): self.testcase_pre = _create_testcase(method, '@%s pre-hook' % self.name) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index c35b068a4..fc5c94771 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -27,9 +27,9 @@ class ResponseMock(object): class CustomFormContract(Contract): name = 'custom_form' + request_cls = FormRequest def adjust_request_args(self, args): - args['request_cls'] = FormRequest args['formdata'] = {'name': 'scrapy'} return args From 25ac4691b414e7c18a4e9dec3bb6a85563d2488d Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Thu, 9 Aug 2018 03:32:46 +0500 Subject: [PATCH 62/79] require parsel 1.5+ --- requirements-py2.txt | 2 +- requirements-py3.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements-py2.txt b/requirements-py2.txt index 03b33d02d..0771aae3a 100644 --- a/requirements-py2.txt +++ b/requirements-py2.txt @@ -6,5 +6,5 @@ queuelib w3lib>=1.17.0 six>=1.5.2 PyDispatcher>=2.0.5 -parsel>=1.4 +parsel>=1.5 service_identity diff --git a/requirements-py3.txt b/requirements-py3.txt index b38c4cc09..5a5d4c95a 100644 --- a/requirements-py3.txt +++ b/requirements-py3.txt @@ -6,5 +6,5 @@ queuelib>=1.1.1 w3lib>=1.17.0 six>=1.5.2 PyDispatcher>=2.0.5 -parsel>=1.4 +parsel>=1.5 service_identity diff --git a/setup.py b/setup.py index c37919cda..8c47f67ce 100644 --- a/setup.py +++ b/setup.py @@ -71,7 +71,7 @@ setup( 'pyOpenSSL', 'cssselect>=0.9', 'six>=1.5.2', - 'parsel>=1.4', + 'parsel>=1.5', 'PyDispatcher>=2.0.5', 'service_identity', ], From 0ccead6681c9e2bf1902cbc1d4bde543be7d73e7 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Wed, 15 Aug 2018 16:16:36 +0500 Subject: [PATCH 63/79] DOC more Python 3 in examples --- docs/topics/commands.rst | 6 ++--- docs/topics/items.rst | 6 ++--- docs/topics/jobs.rst | 4 ++-- docs/topics/loaders.rst | 14 ++++++------ docs/topics/selectors.rst | 48 +++++++++++++++++++-------------------- docs/topics/settings.rst | 2 +- 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/docs/topics/commands.rst b/docs/topics/commands.rst index 3088017cb..ef9c45196 100644 --- a/docs/topics/commands.rst +++ b/docs/topics/commands.rst @@ -458,9 +458,9 @@ Usage example:: >>> STATUS DEPTH LEVEL 1 <<< # Scraped Items ------------------------------------------------------------ - [{'name': u'Example item', - 'category': u'Furniture', - 'length': u'12 cm'}] + [{'name': 'Example item', + 'category': 'Furniture', + 'length': '12 cm'}] # Requests ----------------------------------------------------------------- [] diff --git a/docs/topics/items.rst b/docs/topics/items.rst index 4423bbda2..ae44aecd3 100644 --- a/docs/topics/items.rst +++ b/docs/topics/items.rst @@ -86,7 +86,7 @@ Creating items :: >>> product = Product(name='Desktop PC', price=1000) - >>> print product + >>> print(product) Product(name='Desktop PC', price=1000) Getting field values @@ -161,11 +161,11 @@ Other common tasks Copying items:: >>> product2 = Product(product) - >>> print product2 + >>> print(product2) Product(name='Desktop PC', price=1000) >>> product3 = product2.copy() - >>> print product3 + >>> print(product3) Product(name='Desktop PC', price=1000) Creating dicts from items:: diff --git a/docs/topics/jobs.rst b/docs/topics/jobs.rst index 06c7fff3d..8e1574376 100644 --- a/docs/topics/jobs.rst +++ b/docs/topics/jobs.rst @@ -84,7 +84,7 @@ So, for example, this won't work:: return scrapy.Request('http://www.example.com', callback=lambda r: self.other_callback(r, somearg)) def other_callback(self, response, somearg): - print "the argument passed is:", somearg + print("the argument passed is: %s" % somearg) But this will:: @@ -94,7 +94,7 @@ But this will:: def other_callback(self, response): somearg = response.meta['somearg'] - print "the argument passed is:", somearg + print("the argument passed is: %s" % somearg) If you wish to log the requests that couldn't be serialized, you can set the :setting:`SCHEDULER_DEBUG` setting to ``True`` in the project's settings page. diff --git a/docs/topics/loaders.rst b/docs/topics/loaders.rst index a895b535c..f3b6aa4a1 100644 --- a/docs/topics/loaders.rst +++ b/docs/topics/loaders.rst @@ -678,10 +678,10 @@ Here is a list of all built-in processors: >>> from scrapy.loader.processors import Join >>> proc = Join() >>> proc(['one', 'two', 'three']) - u'one two three' + 'one two three' >>> proc = Join('
') >>> proc(['one', 'two', 'three']) - u'one
two
three' + 'one
two
three' .. class:: Compose(\*functions, \**default_loader_context) @@ -744,9 +744,9 @@ Here is a list of all built-in processors: ... return None if x == 'world' else x ... >>> from scrapy.loader.processors import MapCompose - >>> proc = MapCompose(filter_world, unicode.upper) - >>> proc([u'hello', u'world', u'this', u'is', u'scrapy']) - [u'HELLO, u'THIS', u'IS', u'SCRAPY'] + >>> proc = MapCompose(filter_world, str.upper) + >>> proc(['hello', 'world', 'this', 'is', 'scrapy']) + ['HELLO, 'THIS', 'IS', 'SCRAPY'] As with the Compose processor, functions can receive Loader contexts, and constructor keyword arguments are used as default context values. See @@ -772,7 +772,7 @@ Here is a list of all built-in processors: >>> import json >>> proc_single_json_str = Compose(json.loads, SelectJmes("foo")) >>> proc_single_json_str('{"foo": "bar"}') - u'bar' + 'bar' >>> proc_json_list = Compose(json.loads, MapCompose(SelectJmes('foo'))) >>> proc_json_list('[{"foo":"bar"}, {"baz":"tar"}]') - [u'bar'] + ['bar'] diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 8ac40c3cc..25c1f0aab 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -235,17 +235,17 @@ Here's an example used to extract image names from the :ref:`HTML code ` above:: >>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)') - [u'My image 1', - u'My image 2', - u'My image 3', - u'My image 4', - u'My image 5'] + ['My image 1', + 'My image 2', + 'My image 3', + 'My image 4', + 'My image 5'] There's an additional helper reciprocating ``.extract_first()`` for ``.re()``, named ``.re_first()``. Use it to extract just the first matching string:: >>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)') - u'My image 1' + 'My image 1' .. _topics-selectors-relative-xpaths: @@ -431,26 +431,26 @@ with groups of itemscopes and corresponding itemprops:: ... print " properties:", props.extract() ... print - current scope: [u'http://schema.org/Product'] - properties: [u'name', u'aggregateRating', u'offers', u'description', u'review', u'review'] + current scope: ['http://schema.org/Product'] + properties: ['name', 'aggregateRating', 'offers', 'description', 'review', 'review'] - current scope: [u'http://schema.org/AggregateRating'] - properties: [u'ratingValue', u'reviewCount'] + current scope: ['http://schema.org/AggregateRating'] + properties: ['ratingValue', 'reviewCount'] - current scope: [u'http://schema.org/Offer'] - properties: [u'price', u'availability'] + current scope: ['http://schema.org/Offer'] + properties: ['price', 'availability'] - current scope: [u'http://schema.org/Review'] - properties: [u'name', u'author', u'datePublished', u'reviewRating', u'description'] + current scope: ['http://schema.org/Review'] + properties: ['name', 'author', 'datePublished', 'reviewRating', 'description'] - current scope: [u'http://schema.org/Rating'] - properties: [u'worstRating', u'ratingValue', u'bestRating'] + current scope: ['http://schema.org/Rating'] + properties: ['worstRating', 'ratingValue', 'bestRating'] - current scope: [u'http://schema.org/Review'] - properties: [u'name', u'author', u'datePublished', u'reviewRating', u'description'] + current scope: ['http://schema.org/Review'] + properties: ['name', 'author', 'datePublished', 'reviewRating', 'description'] - current scope: [u'http://schema.org/Rating'] - properties: [u'worstRating', u'ratingValue', u'bestRating'] + current scope: ['http://schema.org/Rating'] + properties: ['worstRating', 'ratingValue', 'bestRating'] >>> @@ -543,22 +543,22 @@ Example:: This gets all first ``
  • `` elements under whatever it is its parent:: >>> xp("//li[1]") - [u'
  • 1
  • ', u'
  • 4
  • '] + ['
  • 1
  • ', '
  • 4
  • '] And this gets the first ``
  • `` element in the whole document:: >>> xp("(//li)[1]") - [u'
  • 1
  • '] + ['
  • 1
  • '] This gets all first ``
  • `` elements under an ``
      `` parent:: >>> xp("//ul/li[1]") - [u'
    • 1
    • ', u'
    • 4
    • '] + ['
    • 1
    • ', '
    • 4
    • '] And this gets the first ``
    • `` element under an ``
        `` parent in the whole document:: >>> xp("(//ul/li)[1]") - [u'
      • 1
      • '] + ['
      • 1
      • '] When querying by class, consider using CSS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/settings.rst b/docs/topics/settings.rst index 1f1217770..47b6cf13d 100644 --- a/docs/topics/settings.rst +++ b/docs/topics/settings.rst @@ -871,7 +871,7 @@ LOG_STDOUT Default: ``False`` If ``True``, all standard output (and error) of your process will be redirected -to the log. For example if you ``print 'hello'`` it will appear in the Scrapy +to the log. For example if you ``print('hello')`` it will appear in the Scrapy log. .. setting:: LOG_SHORT_NAMES From 395d9d033a39ae9e82337ce50cffabd1ecedb702 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Wed, 15 Aug 2018 16:16:57 +0500 Subject: [PATCH 64/79] add pytest temp files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ff6e2ea65..7392ed31e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ htmlcov/ .pytest_cache/ .coverage.* .cache/ +.pytest_cache/ # Windows Thumbs.db From ca27010d4f8c35d1c98259e56be797a18b044304 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Wed, 15 Aug 2018 16:22:56 +0500 Subject: [PATCH 65/79] DOC .extract_first() -> .get() --- docs/intro/overview.rst | 6 ++--- docs/intro/tutorial.rst | 49 +++++++++++++++++++++-------------------- docs/topics/shell.rst | 2 +- docs/topics/spiders.rst | 10 ++++----- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/docs/intro/overview.rst b/docs/intro/overview.rst index 6f1c2c43f..9d7c94d39 100644 --- a/docs/intro/overview.rst +++ b/docs/intro/overview.rst @@ -34,11 +34,11 @@ http://quotes.toscrape.com, following the pagination:: def parse(self, response): for quote in response.css('div.quote'): yield { - 'text': quote.css('span.text::text').extract_first(), - 'author': quote.xpath('span/small/text()').extract_first(), + 'text': quote.css('span.text::text').get(), + 'author': quote.xpath('span/small/text()').get(), } - next_page = response.css('li.next a::attr("href")').extract_first() + next_page = response.css('li.next a::attr("href")').get() if next_page is not None: yield response.follow(next_page, self.parse) diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index fa6dc274d..a24cf0f5b 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -269,23 +269,24 @@ The other thing is that the result of calling ``.extract()`` is a list, because we're dealing with an instance of :class:`~scrapy.selector.SelectorList`. When you know you just want the first result, as in this case, you can do:: - >>> response.css('title::text').extract_first() + >>> response.css('title::text').get() 'Quotes to Scrape' As an alternative, you could've written:: - >>> response.css('title::text')[0].extract() + >>> response.css('title::text')[0].get() 'Quotes to Scrape' -However, using ``.extract_first()`` avoids an ``IndexError`` and returns -``None`` when it doesn't find any element matching the selection. +However, using ``.get()`` directly on a :class:`~scrapy.selector.SelectorList` +instance avoids an ``IndexError`` and returns ``None`` when it doesn't +find any element matching the selection. There's a lesson here: for most scraping code, you want it to be resilient to errors due to things not being found on a page, so that even if some parts fail to be scraped, you can at least get **some** data. Besides the :meth:`~scrapy.selector.Selector.extract` and -:meth:`~scrapy.selector.SelectorList.extract_first` methods, you can also use +:meth:`~scrapy.selector.SelectorList.get` methods, you can also use the :meth:`~scrapy.selector.Selector.re` method to extract using `regular expressions`:: @@ -314,7 +315,7 @@ Besides `CSS`_, Scrapy selectors also support using `XPath`_ expressions:: >>> response.xpath('//title') [] - >>> response.xpath('//title/text()').extract_first() + >>> response.xpath('//title/text()').get() 'Quotes to Scrape' XPath expressions are very powerful, and are the foundation of Scrapy @@ -383,10 +384,10 @@ variable, so that we can run our CSS selectors directly on a particular quote:: Now, let's extract ``title``, ``author`` and the ``tags`` from that quote using the ``quote`` object we just created:: - >>> title = quote.css("span.text::text").extract_first() + >>> title = quote.css("span.text::text").get() >>> title '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”' - >>> author = quote.css("small.author::text").extract_first() + >>> author = quote.css("small.author::text").get() >>> author 'Albert Einstein' @@ -401,8 +402,8 @@ Having figured out how to extract each bit, we can now iterate over all the quotes elements and put them together into a Python dictionary:: >>> for quote in response.css("div.quote"): - ... text = quote.css("span.text::text").extract_first() - ... author = quote.css("small.author::text").extract_first() + ... text = quote.css("span.text::text").get() + ... author = quote.css("small.author::text").get() ... tags = quote.css("div.tags a.tag::text").extract() ... print(dict(text=text, author=author, tags=tags)) {'tags': ['change', 'deep-thoughts', 'thinking', 'world'], 'author': 'Albert Einstein', 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'} @@ -434,8 +435,8 @@ in the callback, as you can see below:: def parse(self, response): for quote in response.css('div.quote'): yield { - 'text': quote.css('span.text::text').extract_first(), - 'author': quote.css('small.author::text').extract_first(), + 'text': quote.css('span.text::text').get(), + 'author': quote.css('small.author::text').get(), 'tags': quote.css('div.tags a.tag::text').extract(), } @@ -508,14 +509,14 @@ markup: We can try extracting it in the shell:: - >>> response.css('li.next a').extract_first() + >>> response.css('li.next a').get() 'Next ' This gets the anchor element, but we want the attribute ``href``. For that, Scrapy supports a CSS extension that let's you select the attribute contents, like this:: - >>> response.css('li.next a::attr(href)').extract_first() + >>> response.css('li.next a::attr(href)').get() '/page/2/' Let's see now our spider modified to recursively follow the link to the next @@ -533,12 +534,12 @@ page, extracting data from it:: def parse(self, response): for quote in response.css('div.quote'): yield { - 'text': quote.css('span.text::text').extract_first(), - 'author': quote.css('small.author::text').extract_first(), + 'text': quote.css('span.text::text').get(), + 'author': quote.css('small.author::text').get(), 'tags': quote.css('div.tags a.tag::text').extract(), } - next_page = response.css('li.next a::attr(href)').extract_first() + next_page = response.css('li.next a::attr(href)').get() if next_page is not None: next_page = response.urljoin(next_page) yield scrapy.Request(next_page, callback=self.parse) @@ -584,12 +585,12 @@ As a shortcut for creating Request objects you can use def parse(self, response): for quote in response.css('div.quote'): yield { - 'text': quote.css('span.text::text').extract_first(), - 'author': quote.css('span small::text').extract_first(), + 'text': quote.css('span.text::text').get(), + 'author': quote.css('span small::text').get(), 'tags': quote.css('div.tags a.tag::text').extract(), } - next_page = response.css('li.next a::attr(href)').extract_first() + next_page = response.css('li.next a::attr(href)').get() if next_page is not None: yield response.follow(next_page, callback=self.parse) @@ -641,7 +642,7 @@ this time for scraping author information:: def parse_author(self, response): def extract_with_css(query): - return response.css(query).extract_first().strip() + return response.css(query).get().strip() yield { 'name': extract_with_css('h3.author-title::text'), @@ -710,11 +711,11 @@ with a specific tag, building the URL based on the argument:: def parse(self, response): for quote in response.css('div.quote'): yield { - 'text': quote.css('span.text::text').extract_first(), - 'author': quote.css('small.author::text').extract_first(), + 'text': quote.css('span.text::text').get(), + 'author': quote.css('small.author::text').get(), } - next_page = response.css('li.next a::attr(href)').extract_first() + next_page = response.css('li.next a::attr(href)').get() if next_page is not None: yield response.follow(next_page, self.parse) diff --git a/docs/topics/shell.rst b/docs/topics/shell.rst index 11ab199f2..9de6abef7 100644 --- a/docs/topics/shell.rst +++ b/docs/topics/shell.rst @@ -179,7 +179,7 @@ all start with the ``[s]`` prefix):: After that, we can start playing with the objects:: - >>> response.xpath('//title/text()').extract_first() + >>> response.xpath('//title/text()').get() 'Scrapy | A Fast and Powerful Scraping and Web Crawling Framework' >>> fetch("https://reddit.com") diff --git a/docs/topics/spiders.rst b/docs/topics/spiders.rst index 697732b47..4505b7315 100644 --- a/docs/topics/spiders.rst +++ b/docs/topics/spiders.rst @@ -434,8 +434,8 @@ Let's now take a look at an example CrawlSpider with rules:: self.logger.info('Hi, this is an item page! %s', response.url) item = scrapy.Item() item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)') - item['name'] = response.xpath('//td[@id="item_name"]/text()').extract() - item['description'] = response.xpath('//td[@id="item_description"]/text()').extract() + item['name'] = response.xpath('//td[@id="item_name"]/text()').get() + item['description'] = response.xpath('//td[@id="item_description"]/text()').get() return item @@ -548,9 +548,9 @@ These spiders are pretty easy to use, let's have a look at one example:: self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.extract())) item = TestItem() - item['id'] = node.xpath('@id').extract() - item['name'] = node.xpath('name').extract() - item['description'] = node.xpath('description').extract() + item['id'] = node.xpath('@id').get() + item['name'] = node.xpath('name').get() + item['description'] = node.xpath('description').get() return item Basically what we did up there was to create a spider that downloads a feed from From d32c4deaa99bf0155542d1b508b5535065b5d7a9 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Wed, 15 Aug 2018 16:23:31 +0500 Subject: [PATCH 66/79] DOC update Scrapy selectors tutorial to match parsel's tutorial better --- docs/topics/selectors.rst | 471 +++++++++++++++++++++++++++----------- 1 file changed, 340 insertions(+), 131 deletions(-) diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 25c1f0aab..00158ecf1 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -6,7 +6,7 @@ Selectors When you're scraping web pages, the most common task you need to perform is to extract data from the HTML source. There are several libraries available to -achieve this: +achieve this, such as: * `BeautifulSoup`_ is a very popular web scraping library among Python programmers which constructs a Python object based on the structure of the @@ -25,8 +25,9 @@ either by `XPath`_ or `CSS`_ expressions. used with HTML. `CSS`_ is a language for applying styles to HTML documents. It defines selectors to associate those styles with specific HTML elements. -Scrapy selectors are built over the `lxml`_ library, which means they're very -similar in speed and parsing accuracy. +Scrapy selectors are powered by `parsel`_ library, which uses `lxml`_ library +under the hood. It means Scrapy selectors are very similar in speed and +parsing accuracy to lxml. This page explains how selectors work and describes their API which is very small and simple, unlike the `lxml`_ API which is much bigger because the @@ -42,7 +43,7 @@ For a complete reference of the selectors API see .. _cssselect: https://pypi.python.org/pypi/cssselect/ .. _XPath: https://www.w3.org/TR/xpath .. _CSS: https://www.w3.org/TR/selectors - +.. _parsel: https://parsel.readthedocs.io/ Using selectors =============== @@ -63,21 +64,32 @@ input type:: Constructing from text:: >>> body = 'good' - >>> Selector(text=body).xpath('//span/text()').extract() - [u'good'] + >>> Selector(text=body).xpath('//span/text()').get() + 'good' Constructing from response:: >>> response = HtmlResponse(url='http://example.com', body=body) - >>> Selector(response=response).xpath('//span/text()').extract() - [u'good'] + >>> Selector(response=response).xpath('//span/text()').get() + 'good' For convenience, response objects expose a selector on `.selector` attribute, -it's totally OK to use this shortcut when possible:: +it's totally OK to use this shortcut when possible. By using it you can +ensure the response body is parsed only once:: - >>> response.selector.xpath('//span/text()').extract() - [u'good'] + >>> response.selector.xpath('//span/text()').get() + 'good' +Querying responses using XPath and CSS is so common that responses include two +more shortcuts: ``response.xpath()`` and ``response.css()``:: + + >>> response.xpath('//span/text()').get() + 'good' + >>> response.css('span::text').get() + 'good' + +Usually there is no need to construct Scrapy selectors manually because of +these shortcuts. Using selectors --------------- @@ -90,7 +102,7 @@ documentation server: .. _topics-selectors-htmlcode: -Here's its HTML code: +For the sake of completeness, here's its full HTML code: .. literalinclude:: ../_static/selectors-sample1.html :language: html @@ -111,90 +123,179 @@ Since we're dealing with HTML, the selector will automatically use an HTML parse So, by looking at the :ref:`HTML code ` of that page, let's construct an XPath for selecting the text inside the title tag:: - >>> response.selector.xpath('//title/text()') - [] - -Querying responses using XPath and CSS is so common that responses include two -convenience shortcuts: ``response.xpath()`` and ``response.css()``:: - >>> response.xpath('//title/text()') - [] - >>> response.css('title::text') - [] + [] + +To actually extract the textual data, you must call the selector ``.get()`` +or ``.getall()`` methods, as follows:: + + >>> response.xpath('//title/text()').getall() + ['Example website'] + >>> response.xpath('//title/text()').get() + 'Example website' + +``.get()`` always returns a single result; if there are several matches, +content of a first match is returned; if there are no matches, None +is returned. ``.getall()`` returns a list with all results. + +Notice that CSS selectors can select text or attribute nodes using CSS3 +pseudo-elements:: + + >>> selector.css('title::text').get() + 'Example website' As you can see, ``.xpath()`` and ``.css()`` methods return a :class:`~scrapy.selector.SelectorList` instance, which is a list of new selectors. This API can be used for quickly selecting nested data:: - >>> response.css('img').xpath('@src').extract() - [u'image1_thumb.jpg', - u'image2_thumb.jpg', - u'image3_thumb.jpg', - u'image4_thumb.jpg', - u'image5_thumb.jpg'] + >>> response.css('img').xpath('@src').getall() + ['image1_thumb.jpg', + 'image2_thumb.jpg', + 'image3_thumb.jpg', + 'image4_thumb.jpg', + 'image5_thumb.jpg'] -To actually extract the textual data, you must call the selector ``.extract()`` -method, as follows:: +If you want to extract only the first matched element, you can call the +selector ``.get()`` (or its alias ``.extract_first()`` commonly used in +previous Scrapy versions):: - >>> response.xpath('//title/text()').extract() - [u'Example website'] + >>> response.xpath('//div[@id="images"]/a/text()').get() + 'Name: My image 1 ' -If you want to extract only first matched element, you can call the selector ``.extract_first()`` +It returns ``None`` if no element was found:: - >>> response.xpath('//div[@id="images"]/a/text()').extract_first() - u'Name: My image 1 ' - -It returns ``None`` if no element was found: - - >>> response.xpath('//div[@id="not-exists"]/text()').extract_first() is None + >>> response.xpath('//div[@id="not-exists"]/text()').get() is None True -A default return value can be provided as an argument, to be used instead of ``None``: +A default return value can be provided as an argument, to be used instead +of ``None``: - >>> response.xpath('//div[@id="not-exists"]/text()').extract_first(default='not-found') + >>> response.xpath('//div[@id="not-exists"]/text()').get(default='not-found') 'not-found' -Notice that CSS selectors can select text or attribute nodes using CSS3 -pseudo-elements:: +Instead of using e.g. ``'@src'`` XPath it is possible to query for attributes +using ``.attrib`` property of a :class:`~scrapy.selector.Selector`:: - >>> response.css('title::text').extract() - [u'Example website'] + >>> [img.attrib['src'] for img in response.css('img')] + ['image1_thumb.jpg', + 'image2_thumb.jpg', + 'image3_thumb.jpg', + 'image4_thumb.jpg', + 'image5_thumb.jpg'] + +As a shortcut, ``.attrib`` is also available on SelectorList directly; +it returns attributes for the first matching element:: + + >>> response.css('img').attrib['src'] + 'image1_thumb.jpg' + +This is most useful when only a single result is expected, e.g. when selecting +by id, or selecting unique elements on a web page:: + + >>> response.css('base').attrib['href'] + 'http://example.com/' Now we're going to get the base URL and some image links:: - >>> response.xpath('//base/@href').extract() - [u'http://example.com/'] + >>> response.xpath('//base/@href').get() + 'http://example.com/' - >>> response.css('base::attr(href)').extract() - [u'http://example.com/'] + >>> response.css('base::attr(href)').get() + 'http://example.com/' - >>> response.xpath('//a[contains(@href, "image")]/@href').extract() - [u'image1.html', - u'image2.html', - u'image3.html', - u'image4.html', - u'image5.html'] + >>> response.css('base').attrib['href'] + 'http://example.com/' - >>> response.css('a[href*=image]::attr(href)').extract() - [u'image1.html', - u'image2.html', - u'image3.html', - u'image4.html', - u'image5.html'] + >>> response.xpath('//a[contains(@href, "image")]/@href').getall() + ['image1.html', + 'image2.html', + 'image3.html', + 'image4.html', + 'image5.html'] - >>> response.xpath('//a[contains(@href, "image")]/img/@src').extract() - [u'image1_thumb.jpg', - u'image2_thumb.jpg', - u'image3_thumb.jpg', - u'image4_thumb.jpg', - u'image5_thumb.jpg'] + >>> response.css('a[href*=image]::attr(href)').getall() + ['image1.html', + 'image2.html', + 'image3.html', + 'image4.html', + 'image5.html'] - >>> response.css('a[href*=image] img::attr(src)').extract() - [u'image1_thumb.jpg', - u'image2_thumb.jpg', - u'image3_thumb.jpg', - u'image4_thumb.jpg', - u'image5_thumb.jpg'] + >>> response.xpath('//a[contains(@href, "image")]/img/@src').getall() + ['image1_thumb.jpg', + 'image2_thumb.jpg', + 'image3_thumb.jpg', + 'image4_thumb.jpg', + 'image5_thumb.jpg'] + + >>> response.css('a[href*=image] img::attr(src)').getall() + ['image1_thumb.jpg', + 'image2_thumb.jpg', + 'image3_thumb.jpg', + 'image4_thumb.jpg', + 'image5_thumb.jpg'] + +.. _topics-selectors-css-extensions: + +Extensions to CSS Selectors +--------------------------- + +Per W3C standards, `CSS selectors`_ do not support selecting text nodes +or attribute values. +But selecting these is so essential in a web scraping context +that Scrapy (parsel) implements a couple of **non-standard pseudo-elements**: + +* to select text nodes, use ``::text`` +* to select attribute values, use ``::attr(name)`` where *name* is the + name of the attribute that you want the value of + +.. warning:: + These pseudo-elements are Scrapy-/Parsel-specific. + They will most probably not work with other libraries like + `lxml`_ or `PyQuery`_. + +.. _PyQuery: https://pypi.python.org/pypi/pyquery + +Examples: + +* ``title::text`` selects children text nodes of a descendant ```` element:: + + >>> response.css('title::text').get() + 'Example website' + +* ``*::text`` selects all descendant text nodes of the current selector context:: + + >>> response.css('#images *::text').getall() + ['\n ', + 'Name: My image 1 ', + '\n ', + 'Name: My image 2 ', + '\n ', + 'Name: My image 3 ', + '\n ', + 'Name: My image 4 ', + '\n ', + 'Name: My image 5 ', + '\n '] + +* ``a::attr(href)`` selects the *href* attribute value of descendant links:: + + >>> response.css('a::attr(href)').getall() + ['image1.html', + 'image2.html', + 'image3.html', + 'image4.html', + 'image5.html'] + +.. note:: + You cannot chain these pseudo-elements. But in practice it would not + make much sense: text nodes do not have attributes, and attribute values + are string values already and do not have children nodes. + +.. note:: + See also: :ref:`selecting-attributes`. + + +.. _CSS Selectors: https://www.w3.org/TR/css3-selectors/#selectors .. _topics-selectors-nesting-selectors: @@ -206,22 +307,65 @@ of the same type, so you can call the selection methods for those selectors too. Here's an example:: >>> links = response.xpath('//a[contains(@href, "image")]') - >>> links.extract() - [u'<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', - u'<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', - u'<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', - u'<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', - u'<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>'] + >>> links.getall() + ['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', + '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', + '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', + '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', + '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>'] >>> for index, link in enumerate(links): - ... args = (index, link.xpath('@href').extract(), link.xpath('img/@src').extract()) - ... print 'Link number %d points to url %s and image %s' % args + ... args = (index, link.xpath('@href').get(), link.xpath('img/@src').get()) + ... print('Link number %d points to url %r and image %r' % args) - Link number 0 points to url [u'image1.html'] and image [u'image1_thumb.jpg'] - Link number 1 points to url [u'image2.html'] and image [u'image2_thumb.jpg'] - Link number 2 points to url [u'image3.html'] and image [u'image3_thumb.jpg'] - Link number 3 points to url [u'image4.html'] and image [u'image4_thumb.jpg'] - Link number 4 points to url [u'image5.html'] and image [u'image5_thumb.jpg'] + Link number 0 points to url 'image1.html' and image 'image1_thumb.jpg' + Link number 1 points to url 'image2.html' and image 'image2_thumb.jpg' + Link number 2 points to url 'image3.html' and image 'image3_thumb.jpg' + Link number 3 points to url 'image4.html' and image 'image4_thumb.jpg' + Link number 4 points to url 'image5.html' and image 'image5_thumb.jpg' + +.. _selecting-attributes: + +Selecting element attributes +---------------------------- + +There are several ways to get a value of an attribute. First, one can use +XPath syntax:: + + >>> response.xpath("//a/@href").getall() + ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] + +XPath syntax has a few advantages: it is a standard XPath feature, and +``@attributes`` can be used in other parts of an XPath expression - e.g. +it is possible to filter by attribute value. + +Scrapy also provides an extension to CSS selectors (``::attr(...)``) +which allows to get attribute values:: + + >>> response.css('a::attr(href)').getall() + ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] + +In addition to that, there is a ``.attrib`` property of Selector. +You can use it if you prefer to lookup attributes in Python +code, without using XPaths or CSS extensions:: + + >>> [a.attrib['href'] for a in response.css('a')] + ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] + +This property is also available on SelectorList; it returns a dictionary +with attributes of a first matching element. It is convenient to use when +a selector is expected to give a single result (e.g. when selecting by element +ID, or when selecting an unique element on a page):: + + >>> response.css('base').attrib + {'href': 'http://example.com/'} + >>> response.css('base').attrib['href'] + 'http://example.com/' + +``.attrib`` property of an empty SelectorList is empty:: + + >>> response.css('foo').attrib + {} Using selectors with regular expressions ---------------------------------------- @@ -241,8 +385,9 @@ Here's an example used to extract image names from the :ref:`HTML code 'My image 4', 'My image 5'] -There's an additional helper reciprocating ``.extract_first()`` for ``.re()``, -named ``.re_first()``. Use it to extract just the first matching string:: +There's an additional helper reciprocating ``.get()`` (and its +alias ``.extract_first()``) for ``.re()``, named ``.re_first()``. +Use it to extract just the first matching string:: >>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)') 'My image 1' @@ -266,17 +411,17 @@ it actually extracts all ``<p>`` elements from the document, not only those inside ``<div>`` elements:: >>> for p in divs.xpath('//p'): # this is wrong - gets all <p> from the whole document - ... print p.extract() + ... print(p.get()) This is the proper way to do it (note the dot prefixing the ``.//p`` XPath):: >>> for p in divs.xpath('.//p'): # extracts all <p> inside - ... print p.extract() + ... print(p.get()) Another common case would be to extract all direct ``<p>`` children:: >>> for p in divs.xpath('p'): - ... print p.extract() + ... print(p.get()) For more details about relative XPaths see the `Location Paths`_ section in the XPath specification. @@ -298,14 +443,14 @@ Here's an example to match an element based on its "id" attribute value, without hard-coding it (that was shown previously):: >>> # `$val` used in the expression, a `val` argument needs to be passed - >>> response.xpath('//div[@id=$val]/a/text()', val='images').extract_first() - u'Name: My image 1 ' + >>> response.xpath('//div[@id=$val]/a/text()', val='images').get() + 'Name: My image 1 ' Here's another example, to find the "id" attribute of a ``<div>`` tag containing five ``<a>`` children (here we pass the value ``5`` as an integer):: - >>> response.xpath('//div[count(a)=$cnt]/@id', cnt=5).extract_first() - u'images' + >>> response.xpath('//div[count(a)=$cnt]/@id', cnt=5).get() + 'images' All variable references must have a binding value when calling ``.xpath()`` (otherwise you'll get a ``ValueError: XPath error:`` exception). @@ -314,13 +459,12 @@ This is done by passing as many named arguments as necessary. `parsel`_, the library powering Scrapy selectors, has more details and examples on `XPath variables`_. -.. _parsel: https://parsel.readthedocs.io/ .. _XPath variables: https://parsel.readthedocs.io/en/latest/usage.html#variables-in-xpath-expressions Using EXSLT extensions ---------------------- -Being built atop `lxml`_, Scrapy selectors also support some `EXSLT`_ extensions +Being built atop `lxml`_, Scrapy selectors support some `EXSLT`_ extensions and come with these pre-registered namespaces to use in XPath expressions: @@ -340,7 +484,7 @@ The ``test()`` function, for example, can prove quite useful when XPath's Example selecting links in list item with a "class" attribute ending with a digit:: >>> from scrapy import Selector - >>> doc = """ + >>> doc = u""" ... <div> ... <ul> ... <li class="item-0"><a href="link1.html">first item</a></li> @@ -352,10 +496,10 @@ Example selecting links in list item with a "class" attribute ending with a digi ... </div> ... """ >>> sel = Selector(text=doc, type="html") - >>> sel.xpath('//li//@href').extract() - [u'link1.html', u'link2.html', u'link3.html', u'link4.html', u'link5.html'] - >>> sel.xpath('//li[re:test(@class, "item-\d$")]//@href').extract() - [u'link1.html', u'link2.html', u'link4.html', u'link5.html'] + >>> sel.xpath('//li//@href').getall() + ['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html'] + >>> sel.xpath('//li[re:test(@class, "item-\d$")]//@href').getall() + ['link1.html', 'link2.html', 'link4.html', 'link5.html'] >>> .. warning:: C library ``libxslt`` doesn't natively support EXSLT regular @@ -372,7 +516,7 @@ extracting text elements for example. Example extracting microdata (sample content taken from http://schema.org/Product) with groups of itemscopes and corresponding itemprops:: - >>> doc = """ + >>> doc = u""" ... <div itemscope itemtype="http://schema.org/Product"> ... <span itemprop="name">Kenmore White 17" Microwave</span> ... <img src="kenmore-microwave-17in.jpg" alt='Kenmore 17" Microwave' /> @@ -424,12 +568,12 @@ with groups of itemscopes and corresponding itemprops:: ... """ >>> sel = Selector(text=doc, type="html") >>> for scope in sel.xpath('//div[@itemscope]'): - ... print "current scope:", scope.xpath('@itemtype').extract() + ... print("current scope:", scope.xpath('@itemtype').getall()) ... props = scope.xpath(''' ... set:difference(./descendant::*/@itemprop, ... .//*[@itemscope]/*/@itemprop)''') - ... print " properties:", props.extract() - ... print + ... print(" properties: %s" % (props.getall())) + ... print("") current scope: ['http://schema.org/Product'] properties: ['name', 'aggregateRating', 'offers', 'description', 'review', 'review'] @@ -493,27 +637,27 @@ Example:: Converting a *node-set* to string:: - >>> sel.xpath('//a//text()').extract() # take a peek at the node-set - [u'Click here to go to the ', u'Next Page'] - >>> sel.xpath("string(//a[1]//text())").extract() # convert it to string - [u'Click here to go to the '] + >>> sel.xpath('//a//text()').getall() # take a peek at the node-set + ['Click here to go to the ', 'Next Page'] + >>> sel.xpath("string(//a[1]//text())").getall() # convert it to string + ['Click here to go to the '] A *node* converted to a string, however, puts together the text of itself plus of all its descendants:: - >>> sel.xpath("//a[1]").extract() # select the first node - [u'<a href="#">Click here to go to the <strong>Next Page</strong></a>'] - >>> sel.xpath("string(//a[1])").extract() # convert it to string - [u'Click here to go to the Next Page'] + >>> sel.xpath("//a[1]").getall() # select the first node + ['<a href="#">Click here to go to the <strong>Next Page</strong></a>'] + >>> sel.xpath("string(//a[1])").getall() # convert it to string + ['Click here to go to the Next Page'] So, using the ``.//text()`` node-set won't select anything in this case:: - >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").extract() + >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").getall() [] But using the ``.`` to mean the node, works:: - >>> sel.xpath("//a[contains(., 'Next Page')]").extract() - [u'<a href="#">Click here to go to the <strong>Next Page</strong></a>'] + >>> sel.xpath("//a[contains(., 'Next Page')]").getall() + ['<a href="#">Click here to go to the <strong>Next Page</strong></a>'] .. _`XPath string function`: https://www.w3.org/TR/xpath/#section-String-Functions @@ -538,7 +682,7 @@ Example:: ....: <li>5</li> ....: <li>6</li> ....: </ul>""") - >>> xp = lambda x: sel.xpath(x).extract() + >>> xp = lambda x: sel.xpath(x).getall() This gets all first ``<li>`` elements under whatever it is its parent:: @@ -578,12 +722,59 @@ you can just select by class using CSS and then switch to XPath when needed:: >>> from scrapy import Selector >>> sel = Selector(text='<div class="hero shout"><time datetime="2014-07-23 19:00">Special date</time></div>') - >>> sel.css('.shout').xpath('./time/@datetime').extract() - [u'2014-07-23 19:00'] + >>> sel.css('.shout').xpath('./time/@datetime').getall() + ['2014-07-23 19:00'] This is cleaner than using the verbose XPath trick shown above. Just remember to use the ``.`` in the XPath expressions that will follow. +.. _old-extraction-api: + +extract() and extract_first() +----------------------------- + +If you're a long-time Scrapy user, you're probably familiar +with ``.extract()`` and ``.extract_first()`` selector methods. These methods +are still supported by Scrapy, there are no plans to deprecate them. + +However, Scrapy usage docs are now written using ``.get()`` and +``.getall()`` methods. We feel that these new methods result in a more concise +and readable code. + +The following examples show how these methods map to each other. + +1. ``SelectorList.get()`` is the same as ``SelectorList.extract_first()``:: + + >>> response.css('a::attr(href)').get() + 'image1.html' + >>> response.css('a::attr(href)').extract_first() + 'image1.html' + +2. ``SelectorList.getall()`` is the same as ``SelectorList.extract()``:: + + >>> response.css('a::attr(href)').getall() + ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] + >>> response.css('a::attr(href)').extract() + ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] + +2. ``Selector.get()`` is the same as ``Selector.extract()``:: + + >>> response.css('a::attr(href)')[0].get() + 'image1.html' + >>> response.css('a::attr(href)')[0].extract() + 'image1.html' + +4. For consistency, there is also ``Selector.getall()``, which returns a list:: + + >>> response.css('a::attr(href)')[0].getall() + ['image1.html'] + +So, the main difference is that output of ``.get()`` and ``.getall()`` methods +is more predictable: ``.get()`` always returns a single result, ``.getall()`` +always returns a list of all extracted results. With ``.extract()`` method +it was not always obvious if a result is a list or not; to get a single +result either ``.extract()`` or ``.extract_first()`` should be called. + .. _topics-selectors-ref: @@ -718,10 +909,12 @@ SelectorList objects their results flattened, as a list of unicode strings. +.. _selector-examples-html: + Selector examples on HTML response ---------------------------------- -Here's a couple of :class:`Selector` examples to illustrate several concepts. +Here are some :class:`Selector` examples to illustrate several concepts. In all cases, we assume there is already a :class:`Selector` instantiated with a :class:`~scrapy.http.HtmlResponse` object like this:: @@ -735,20 +928,22 @@ a :class:`~scrapy.http.HtmlResponse` object like this:: 2. Extract the text of all ``<h1>`` elements from an HTML response body, returning a list of unicode strings:: - sel.xpath("//h1").extract() # this includes the h1 tag - sel.xpath("//h1/text()").extract() # this excludes the h1 tag + sel.xpath("//h1").getall() # this includes the h1 tag + sel.xpath("//h1/text()").getall() # this excludes the h1 tag 3. Iterate over all ``<p>`` tags and print their class attribute:: for node in sel.xpath("//p"): - print node.xpath("@class").extract() + print(node.attrib['class']) + + +.. _selector-examples-xml: Selector examples on XML response --------------------------------- -Here's a couple of examples to illustrate several concepts. In both cases we -assume there is already a :class:`Selector` instantiated with an -:class:`~scrapy.http.XmlResponse` object like this:: +Here are some examples to illustrate concepts for :class:`Selector` objects +instantiated with an :class:`~scrapy.http.XmlResponse` object:: sel = Selector(xml_response) @@ -761,7 +956,7 @@ assume there is already a :class:`Selector` instantiated with an a namespace:: sel.register_namespace("g", "http://base.google.com/ns/1.0") - sel.xpath("//g:price").extract() + sel.xpath("//g:price").getall() .. _removing-namespaces: @@ -781,6 +976,20 @@ First, we open the shell with the url we want to scrape:: $ scrapy shell https://github.com/blog.atom +.. highlight:: xml + +This is how the file starts:: + + <?xml version="1.0" encoding="UTF-8"?> + <feed xml:lang="en-US" + xmlns="http://www.w3.org/2005/Atom" + xmlns:media="http://search.yahoo.com/mrss/"> + <id>tag:github.com,2008:/blog</id> + ... + +You can see two namespace declarations: a default "http://www.w3.org/2005/Atom" +and another one using the "media:" prefix for "http://search.yahoo.com/mrss/". + .. highlight:: python Once in the shell we can try selecting all ``<link>`` objects and see that it @@ -794,8 +1003,8 @@ nodes can be accessed directly by their names:: >>> response.selector.remove_namespaces() >>> response.xpath("//link") - [<Selector xpath='//link' data=u'<link xmlns="http://www.w3.org/2005/Atom'>, - <Selector xpath='//link' data=u'<link xmlns="http://www.w3.org/2005/Atom'>, + [<Selector xpath='//link' data='<link xmlns="http://www.w3.org/2005/Atom'>, + <Selector xpath='//link' data='<link xmlns="http://www.w3.org/2005/Atom'>, ... If you wonder why the namespace removal procedure isn't always called by default @@ -803,8 +1012,8 @@ instead of having to call it manually, this is because of two reasons, which, in of relevance, are: 1. Removing namespaces requires to iterate and modify all nodes in the - document, which is a reasonably expensive operation to perform for all - documents crawled by Scrapy + document, which is a reasonably expensive operation to perform by default + for all documents crawled by Scrapy 2. There could be some cases where using namespaces is actually required, in case some element names clash between namespaces. These cases are very rare From 09fd6c2a816ebec175ff3ffd945ce3263c9f1570 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 03:59:58 +0500 Subject: [PATCH 67/79] DOC unlink Firefox & Firebug sections from the tutorial for now. See https://github.com/scrapy/scrapy/issues/3373 and https://github.com/scrapy/scrapy/issues/3372 for motivation. --- docs/intro/tutorial.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index a24cf0f5b..453f5114f 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -299,7 +299,8 @@ expressions`:: In order to find the proper CSS selectors to use, you might find useful opening the response page from the shell in your web browser using ``view(response)``. -You can use your browser developer tools (see section about :ref:`topics-developer-tools`). +You can use your browser developer tools to inspect the HTML and come up +with a selector (see section about :ref:`topics-developer-tools`). `Selector Gadget`_ is also a nice tool to quickly find CSS selector for visually selected elements, which works in many browsers. From 2c48d156db7d7846d80fa379dfb33552ac5b5a85 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 04:01:17 +0500 Subject: [PATCH 68/79] DOC cleanup references in tutorials: * remove unused link * fix ReST syntax * fix a link to regular expression docs --- docs/intro/tutorial.rst | 3 +-- docs/topics/selectors.rst | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index 453f5114f..92d1065af 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -288,7 +288,7 @@ to be scraped, you can at least get **some** data. Besides the :meth:`~scrapy.selector.Selector.extract` and :meth:`~scrapy.selector.SelectorList.get` methods, you can also use the :meth:`~scrapy.selector.Selector.re` method to extract using `regular -expressions`:: +expressions`_:: >>> response.css('title::text').re(r'Quotes.*') ['Quotes to Scrape'] @@ -740,4 +740,3 @@ modeling the scraped data. If you prefer to play with an example project, check the :ref:`intro-examples` section. .. _JSON: https://en.wikipedia.org/wiki/JSON -.. _dirbot: https://github.com/scrapy/dirbot diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 00158ecf1..cf1b0ba22 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -251,7 +251,7 @@ that Scrapy (parsel) implements a couple of **non-standard pseudo-elements**: .. warning:: These pseudo-elements are Scrapy-/Parsel-specific. They will most probably not work with other libraries like - `lxml`_ or `PyQuery`_. + `lxml`_ or `PyQuery`_. .. _PyQuery: https://pypi.python.org/pypi/pyquery From 53da56c8dcd6edba12284ed5d125ddd1dcf6f99c Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 04:17:55 +0500 Subject: [PATCH 69/79] TST update tests to use get/getall/attrib instead of extract --- tests/test_command_shell.py | 2 +- tests/test_http_response.py | 34 +++++++++++----------- tests/test_loader.py | 4 +-- tests/test_pipeline_crawl.py | 2 +- tests/test_selector.py | 24 +++++++-------- tests/test_spider.py | 8 ++--- tests/test_utils_iterators.py | 55 ++++++++++++++++++----------------- 7 files changed, 66 insertions(+), 63 deletions(-) diff --git a/tests/test_command_shell.py b/tests/test_command_shell.py index 3e27d6abd..36baacfbd 100644 --- a/tests/test_command_shell.py +++ b/tests/test_command_shell.py @@ -35,7 +35,7 @@ class ShellTest(ProcessTest, SiteTest, unittest.TestCase): @defer.inlineCallbacks def test_response_selector_html(self): - xpath = 'response.xpath("//p[@class=\'one\']/text()").extract()[0]' + xpath = 'response.xpath("//p[@class=\'one\']/text()").get()' _, out, _ = yield self.execute([self.url('/html'), '-c', xpath]) self.assertEqual(out.strip(), b'Works') diff --git a/tests/test_http_response.py b/tests/test_http_response.py index 820758dc9..3b90e3dac 100644 --- a/tests/test_http_response.py +++ b/tests/test_http_response.py @@ -336,11 +336,11 @@ class TextResponseTest(BaseResponseTest): self.assertIs(response.selector.response, response) self.assertEqual( - response.selector.xpath("//title/text()").extract(), + response.selector.xpath("//title/text()").getall(), [u'Some page'] ) self.assertEqual( - response.selector.css("title::text").extract(), + response.selector.css("title::text").getall(), [u'Some page'] ) self.assertEqual( @@ -353,12 +353,12 @@ class TextResponseTest(BaseResponseTest): response = self.response_class("http://www.example.com", body=body) self.assertEqual( - response.xpath("//title/text()").extract(), - response.selector.xpath("//title/text()").extract(), + response.xpath("//title/text()").getall(), + response.selector.xpath("//title/text()").getall(), ) self.assertEqual( - response.css("title::text").extract(), - response.selector.css("title::text").extract(), + response.css("title::text").getall(), + response.selector.css("title::text").getall(), ) def test_selector_shortcuts_kwargs(self): @@ -366,13 +366,13 @@ class TextResponseTest(BaseResponseTest): response = self.response_class("http://www.example.com", body=body) self.assertEqual( - response.xpath("normalize-space(//p[@class=$pclass])", pclass="content").extract(), - response.xpath("normalize-space(//p[@class=\"content\"])").extract(), + response.xpath("normalize-space(//p[@class=$pclass])", pclass="content").getall(), + response.xpath("normalize-space(//p[@class=\"content\"])").getall(), ) self.assertEqual( response.xpath("//title[count(following::p[@class=$pclass])=$pcount]/text()", - pclass="content", pcount=1).extract(), - response.xpath("//title[count(following::p[@class=\"content\"])=1]/text()").extract(), + pclass="content", pcount=1).getall(), + response.xpath("//title[count(following::p[@class=\"content\"])=1]/text()").getall(), ) def test_urljoin_with_base_url(self): @@ -562,7 +562,7 @@ class XmlResponseTest(TextResponseTest): self.assertIs(response.selector.response, response) self.assertEqual( - response.selector.xpath("//elem/text()").extract(), + response.selector.xpath("//elem/text()").getall(), [u'value'] ) @@ -571,8 +571,8 @@ class XmlResponseTest(TextResponseTest): response = self.response_class("http://www.example.com", body=body) self.assertEqual( - response.xpath("//elem/text()").extract(), - response.selector.xpath("//elem/text()").extract(), + response.xpath("//elem/text()").getall(), + response.selector.xpath("//elem/text()").getall(), ) def test_selector_shortcuts_kwargs(self): @@ -583,12 +583,12 @@ class XmlResponseTest(TextResponseTest): response = self.response_class("http://www.example.com", body=body) self.assertEqual( - response.xpath("//s:elem/text()", namespaces={'s': 'http://scrapy.org'}).extract(), - response.selector.xpath("//s:elem/text()", namespaces={'s': 'http://scrapy.org'}).extract(), + response.xpath("//s:elem/text()", namespaces={'s': 'http://scrapy.org'}).getall(), + response.selector.xpath("//s:elem/text()", namespaces={'s': 'http://scrapy.org'}).getall(), ) response.selector.register_namespace('s2', 'http://scrapy.org') self.assertEqual( - response.xpath("//s1:elem/text()", namespaces={'s1': 'http://scrapy.org'}).extract(), - response.selector.xpath("//s2:elem/text()").extract(), + response.xpath("//s1:elem/text()", namespaces={'s1': 'http://scrapy.org'}).getall(), + response.selector.xpath("//s2:elem/text()").getall(), ) diff --git a/tests/test_loader.py b/tests/test_loader.py index 3b5714058..8b58e4dbd 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -634,7 +634,7 @@ class SubselectorLoaderTest(unittest.TestCase): nl = l.nested_xpath("//header") nl.add_xpath('name', 'div/text()') nl.add_css('name_div', '#id') - nl.add_value('name_value', nl.selector.xpath('div[@id = "id"]/text()').extract()) + nl.add_value('name_value', nl.selector.xpath('div[@id = "id"]/text()').getall()) self.assertEqual(l.get_output_value('name'), [u'marta']) self.assertEqual(l.get_output_value('name_div'), [u'<div id="id">marta</div>']) @@ -649,7 +649,7 @@ class SubselectorLoaderTest(unittest.TestCase): nl = l.nested_css("header") nl.add_xpath('name', 'div/text()') nl.add_css('name_div', '#id') - nl.add_value('name_value', nl.selector.xpath('div[@id = "id"]/text()').extract()) + nl.add_value('name_value', nl.selector.xpath('div[@id = "id"]/text()').getall()) self.assertEqual(l.get_output_value('name'), [u'marta']) self.assertEqual(l.get_output_value('name_div'), [u'<div id="id">marta</div>']) diff --git a/tests/test_pipeline_crawl.py b/tests/test_pipeline_crawl.py index 5985a6f3e..fb72c9d6d 100644 --- a/tests/test_pipeline_crawl.py +++ b/tests/test_pipeline_crawl.py @@ -29,7 +29,7 @@ class MediaDownloadSpider(SimpleSpider): for href in response.xpath(''' //table[thead/tr/th="Filename"] /tbody//a/@href - ''').extract()], + ''').getall()], } yield item diff --git a/tests/test_selector.py b/tests/test_selector.py index 526660cc8..bc4baf7ea 100644 --- a/tests/test_selector.py +++ b/tests/test_selector.py @@ -20,17 +20,17 @@ class SelectorTestCase(unittest.TestCase): for x in xl: assert isinstance(x, Selector) - self.assertEqual(sel.xpath('//input').extract(), - [x.extract() for x in sel.xpath('//input')]) + self.assertEqual(sel.xpath('//input').getall(), + [x.get() for x in sel.xpath('//input')]) - self.assertEqual([x.extract() for x in sel.xpath("//input[@name='a']/@name")], + self.assertEqual([x.get() for x in sel.xpath("//input[@name='a']/@name")], [u'a']) - self.assertEqual([x.extract() for x in sel.xpath("number(concat(//input[@name='a']/@value, //input[@name='b']/@value))")], + self.assertEqual([x.get() for x in sel.xpath("number(concat(//input[@name='a']/@value, //input[@name='b']/@value))")], [u'12.0']) - self.assertEqual(sel.xpath("concat('xpath', 'rules')").extract(), + self.assertEqual(sel.xpath("concat('xpath', 'rules')").getall(), [u'xpathrules']) - self.assertEqual([x.extract() for x in sel.xpath("concat(//input[@name='a']/@value, //input[@name='b']/@value)")], + self.assertEqual([x.get() for x in sel.xpath("concat(//input[@name='a']/@value, //input[@name='b']/@value)")], [u'12']) def test_root_base_url(self): @@ -60,12 +60,12 @@ class SelectorTestCase(unittest.TestCase): text = b'<div><img src="a.jpg"><p>Hello</div>' sel = Selector(XmlResponse('http://example.com', body=text, encoding='utf-8')) self.assertEqual(sel.type, 'xml') - self.assertEqual(sel.xpath("//div").extract(), + self.assertEqual(sel.xpath("//div").getall(), [u'<div><img src="a.jpg"><p>Hello</p></img></div>']) sel = Selector(HtmlResponse('http://example.com', body=text, encoding='utf-8')) self.assertEqual(sel.type, 'html') - self.assertEqual(sel.xpath("//div").extract(), + self.assertEqual(sel.xpath("//div").getall(), [u'<div><img src="a.jpg"><p>Hello</p></div>']) def test_http_header_encoding_precedence(self): @@ -84,15 +84,15 @@ class SelectorTestCase(unittest.TestCase): headers = {'Content-Type': ['text/html; charset=utf-8']} response = HtmlResponse(url="http://example.com", headers=headers, body=html_utf8) x = Selector(response) - self.assertEqual(x.xpath("//span[@id='blank']/text()").extract(), + self.assertEqual(x.xpath("//span[@id='blank']/text()").getall(), [u'\xa3']) def test_badly_encoded_body(self): # \xe9 alone isn't valid utf8 sequence - r1 = TextResponse('http://www.example.com', \ - body=b'<html><p>an Jos\xe9 de</p><html>', \ + r1 = TextResponse('http://www.example.com', + body=b'<html><p>an Jos\xe9 de</p><html>', encoding='utf-8') - Selector(r1).xpath('//text()').extract() + Selector(r1).xpath('//text()').getall() def test_weakref_slots(self): """Check that classes are using slots and are weak-referenceable""" diff --git a/tests/test_spider.py b/tests/test_spider.py index 929e0fea8..f26da2334 100644 --- a/tests/test_spider.py +++ b/tests/test_spider.py @@ -147,10 +147,10 @@ class XMLFeedSpiderTest(SpiderTest): def parse_node(self, response, selector): yield { - 'loc': selector.xpath('a:loc/text()').extract(), - 'updated': selector.xpath('b:updated/text()').extract(), - 'other': selector.xpath('other/@value').extract(), - 'custom': selector.xpath('other/@b:custom').extract(), + 'loc': selector.xpath('a:loc/text()').getall(), + 'updated': selector.xpath('b:updated/text()').getall(), + 'other': selector.xpath('other/@value').getall(), + 'custom': selector.xpath('other/@b:custom').getall(), } for iterator in ('iternodes', 'xml'): diff --git a/tests/test_utils_iterators.py b/tests/test_utils_iterators.py index f953076b8..00eb78068 100644 --- a/tests/test_utils_iterators.py +++ b/tests/test_utils_iterators.py @@ -30,10 +30,13 @@ class XmliterTestCase(unittest.TestCase): response = XmlResponse(url="http://example.com", body=body) attrs = [] for x in self.xmliter(response, 'product'): - attrs.append((x.xpath("@id").extract(), x.xpath("name/text()").extract(), x.xpath("./type/text()").extract())) + attrs.append(( + x.attrib['id'], + x.xpath("name/text()").extract(), + x.xpath("./type/text()").extract())) self.assertEqual(attrs, - [(['001'], ['Name 1'], ['Type 1']), (['002'], ['Name 2'], ['Type 2'])]) + [('001', ['Name 1'], ['Type 1']), ('002', ['Name 2'], ['Type 2'])]) def test_xmliter_unusual_node(self): body = b"""<?xml version="1.0" encoding="UTF-8"?> @@ -43,7 +46,7 @@ class XmliterTestCase(unittest.TestCase): </root> """ response = XmlResponse(url="http://example.com", body=body) - nodenames = [e.xpath('name()').extract() + nodenames = [e.xpath('name()').getall() for e in self.xmliter(response, 'matchme...')] self.assertEqual(nodenames, [['matchme...']]) @@ -93,19 +96,19 @@ class XmliterTestCase(unittest.TestCase): attrs = [] for x in self.xmliter(r, u'þingflokkur'): - attrs.append((x.xpath('@id').extract(), - x.xpath(u'./skammstafanir/stuttskammstöfun/text()').extract(), - x.xpath(u'./tímabil/fyrstaþing/text()').extract())) + attrs.append((x.attrib['id'], + x.xpath(u'./skammstafanir/stuttskammstöfun/text()').getall(), + x.xpath(u'./tímabil/fyrstaþing/text()').getall())) self.assertEqual(attrs, - [([u'26'], [u'-'], [u'80']), - ([u'21'], [u'Ab'], [u'76']), - ([u'27'], [u'A'], [u'27'])]) + [(u'26', [u'-'], [u'80']), + (u'21', [u'Ab'], [u'76']), + (u'27', [u'A'], [u'27'])]) def test_xmliter_text(self): body = u"""<?xml version="1.0" encoding="UTF-8"?><products><product>one</product><product>two</product></products>""" - self.assertEqual([x.xpath("text()").extract() for x in self.xmliter(body, 'product')], + self.assertEqual([x.xpath("text()").getall() for x in self.xmliter(body, 'product')], [[u'one'], [u'two']]) def test_xmliter_namespaces(self): @@ -132,15 +135,15 @@ class XmliterTestCase(unittest.TestCase): node = next(my_iter) node.register_namespace('g', 'http://base.google.com/ns/1.0') - self.assertEqual(node.xpath('title/text()').extract(), ['Item 1']) - self.assertEqual(node.xpath('description/text()').extract(), ['This is item 1']) - self.assertEqual(node.xpath('link/text()').extract(), ['http://www.mydummycompany.com/items/1']) - self.assertEqual(node.xpath('g:image_link/text()').extract(), ['http://www.mydummycompany.com/images/item1.jpg']) - self.assertEqual(node.xpath('g:id/text()').extract(), ['ITEM_1']) - self.assertEqual(node.xpath('g:price/text()').extract(), ['400']) - self.assertEqual(node.xpath('image_link/text()').extract(), []) - self.assertEqual(node.xpath('id/text()').extract(), []) - self.assertEqual(node.xpath('price/text()').extract(), []) + self.assertEqual(node.xpath('title/text()').getall(), ['Item 1']) + self.assertEqual(node.xpath('description/text()').getall(), ['This is item 1']) + self.assertEqual(node.xpath('link/text()').getall(), ['http://www.mydummycompany.com/items/1']) + self.assertEqual(node.xpath('g:image_link/text()').getall(), ['http://www.mydummycompany.com/images/item1.jpg']) + self.assertEqual(node.xpath('g:id/text()').getall(), ['ITEM_1']) + self.assertEqual(node.xpath('g:price/text()').getall(), ['400']) + self.assertEqual(node.xpath('image_link/text()').getall(), []) + self.assertEqual(node.xpath('id/text()').getall(), []) + self.assertEqual(node.xpath('price/text()').getall(), []) def test_xmliter_exception(self): body = u"""<?xml version="1.0" encoding="UTF-8"?><products><product>one</product><product>two</product></products>""" @@ -159,7 +162,7 @@ class XmliterTestCase(unittest.TestCase): body = b'<?xml version="1.0" encoding="ISO-8859-9"?>\n<xml>\n <item>Some Turkish Characters \xd6\xc7\xde\xdd\xd0\xdc \xfc\xf0\xfd\xfe\xe7\xf6</item>\n</xml>\n\n' response = XmlResponse('http://www.example.com', body=body) self.assertEqual( - next(self.xmliter(response, 'item')).extract(), + next(self.xmliter(response, 'item')).get(), u'<item>Some Turkish Characters \xd6\xc7\u015e\u0130\u011e\xdc \xfc\u011f\u0131\u015f\xe7\xf6</item>' ) @@ -192,9 +195,9 @@ class LxmlXmliterTestCase(XmliterTestCase): namespace_iter = self.xmliter(response, 'image_link', 'http://base.google.com/ns/1.0') node = next(namespace_iter) - self.assertEqual(node.xpath('text()').extract(), ['http://www.mydummycompany.com/images/item1.jpg']) + self.assertEqual(node.xpath('text()').getall(), ['http://www.mydummycompany.com/images/item1.jpg']) node = next(namespace_iter) - self.assertEqual(node.xpath('text()').extract(), ['http://www.mydummycompany.com/images/item2.jpg']) + self.assertEqual(node.xpath('text()').getall(), ['http://www.mydummycompany.com/images/item2.jpg']) def test_xmliter_namespaces_prefix(self): body = b"""\ @@ -219,14 +222,14 @@ class LxmlXmliterTestCase(XmliterTestCase): my_iter = self.xmliter(response, 'table', 'http://www.w3.org/TR/html4/', 'h') node = next(my_iter) - self.assertEqual(len(node.xpath('h:tr/h:td').extract()), 2) - self.assertEqual(node.xpath('h:tr/h:td[1]/text()').extract(), ['Apples']) - self.assertEqual(node.xpath('h:tr/h:td[2]/text()').extract(), ['Bananas']) + self.assertEqual(len(node.xpath('h:tr/h:td').getall()), 2) + self.assertEqual(node.xpath('h:tr/h:td[1]/text()').getall(), ['Apples']) + self.assertEqual(node.xpath('h:tr/h:td[2]/text()').getall(), ['Bananas']) my_iter = self.xmliter(response, 'table', 'http://www.w3schools.com/furniture', 'f') node = next(my_iter) - self.assertEqual(node.xpath('f:name/text()').extract(), ['African Coffee Table']) + self.assertEqual(node.xpath('f:name/text()').getall(), ['African Coffee Table']) def test_xmliter_objtype_exception(self): i = self.xmliter(42, 'product') From 8c29be606c1be71059b454e41b87354e22569423 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 04:18:29 +0500 Subject: [PATCH 70/79] update spider templates to use .get --- scrapy/templates/spiders/crawl.tmpl | 10 +++++----- scrapy/templates/spiders/xmlfeed.tmpl | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scrapy/templates/spiders/crawl.tmpl b/scrapy/templates/spiders/crawl.tmpl index 802cb88a1..878425125 100644 --- a/scrapy/templates/spiders/crawl.tmpl +++ b/scrapy/templates/spiders/crawl.tmpl @@ -14,8 +14,8 @@ class $classname(CrawlSpider): ) def parse_item(self, response): - i = {} - #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract() - #i['name'] = response.xpath('//div[@id="name"]').extract() - #i['description'] = response.xpath('//div[@id="description"]').extract() - return i + item = {} + #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get() + #item['name'] = response.xpath('//div[@id="name"]').get() + #item['description'] = response.xpath('//div[@id="description"]').get() + return item diff --git a/scrapy/templates/spiders/xmlfeed.tmpl b/scrapy/templates/spiders/xmlfeed.tmpl index 7c2ff8850..863c9772f 100644 --- a/scrapy/templates/spiders/xmlfeed.tmpl +++ b/scrapy/templates/spiders/xmlfeed.tmpl @@ -10,8 +10,8 @@ class $classname(XMLFeedSpider): itertag = 'item' # change it accordingly def parse_node(self, response, selector): - i = {} - #i['url'] = selector.select('url').extract() - #i['name'] = selector.select('name').extract() - #i['description'] = selector.select('description').extract() - return i + item = {} + #item['url'] = selector.select('url').get() + #item['name'] = selector.select('name').get() + #item['description'] = selector.select('description').get() + return item From 460f0f045141f73dcc6d7a2309b7e2f0c2a492b4 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 04:20:32 +0500 Subject: [PATCH 71/79] [backwards incompatible] switch ItemLoader from .extract to .getall. This change is backwards incompatible if ItemLoader is used with a custom Selector subclass which overrides .extract without overriding .getall. --- scrapy/loader/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scrapy/loader/__init__.py b/scrapy/loader/__init__.py index e73413318..a7c75a46a 100644 --- a/scrapy/loader/__init__.py +++ b/scrapy/loader/__init__.py @@ -181,7 +181,7 @@ class ItemLoader(object): def _get_xpathvalues(self, xpaths, **kw): self._check_selector_method() xpaths = arg_to_iter(xpaths) - return flatten(self.selector.xpath(xpath).extract() for xpath in xpaths) + return flatten(self.selector.xpath(xpath).getall() for xpath in xpaths) def add_css(self, field_name, css, *processors, **kw): values = self._get_cssvalues(css, **kw) @@ -198,6 +198,6 @@ class ItemLoader(object): def _get_cssvalues(self, csss, **kw): self._check_selector_method() csss = arg_to_iter(csss) - return flatten(self.selector.css(css).extract() for css in csss) + return flatten(self.selector.css(css).getall() for css in csss) XPathItemLoader = create_deprecated_class('XPathItemLoader', ItemLoader) From 12e42bbe06bea5a0f86e95630b184e86b41c95c7 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 04:20:55 +0500 Subject: [PATCH 72/79] switch SgmlLinkExtractor to .getall --- scrapy/linkextractors/sgml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scrapy/linkextractors/sgml.py b/scrapy/linkextractors/sgml.py index f4ca4262a..5fa6b771c 100644 --- a/scrapy/linkextractors/sgml.py +++ b/scrapy/linkextractors/sgml.py @@ -141,7 +141,7 @@ class SgmlLinkExtractor(FilteringLinkExtractor): base_url = get_base_url(response) body = u''.join(f for x in self.restrict_xpaths - for f in response.xpath(x).extract() + for f in response.xpath(x).getall() ).encode(response.encoding, errors='xmlcharrefreplace') else: body = response.body From afce9716fabff6169dc9cc146a2589ebf4c1ed13 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 04:25:43 +0500 Subject: [PATCH 73/79] DOC mention .attrib in the tutorial --- docs/intro/tutorial.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index 92d1065af..07fb4807f 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -520,6 +520,12 @@ like this:: >>> response.css('li.next a::attr(href)').get() '/page/2/' +There is also an ``attrib`` property available +(see :ref:`selecting-attributes` for more):: + + >>> response.css('li.next a').attrib['href'] + '/page/2' + Let's see now our spider modified to recursively follow the link to the next page, extracting data from it:: From bdcc045f62a34d1655a4ead4e0b38d6fe97a08fc Mon Sep 17 00:00:00 2001 From: Mikhail Korobov <kmike84@gmail.com> Date: Wed, 22 Aug 2018 04:27:21 +0500 Subject: [PATCH 74/79] DOC switch from .extract to get/getall API in docs Also, response.urljoin is added in a few places, for robustness. --- docs/intro/tutorial.rst | 22 +++++++++++----------- docs/topics/shell.rst | 4 ++-- docs/topics/spiders.rst | 14 +++++++------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index 07fb4807f..46e84b21c 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -254,7 +254,7 @@ data. To extract the text from the title above, you can do:: - >>> response.css('title::text').extract() + >>> response.css('title::text').getall() ['Quotes to Scrape'] There are two things to note here: one is that we've added ``::text`` to the @@ -262,12 +262,12 @@ CSS query, to mean we want to select only the text elements directly inside ``<title>`` element. If we don't specify ``::text``, we'd get the full title element, including its tags:: - >>> response.css('title').extract() + >>> response.css('title').getall() ['<title>Quotes to Scrape'] -The other thing is that the result of calling ``.extract()`` is a list, because -we're dealing with an instance of :class:`~scrapy.selector.SelectorList`. When -you know you just want the first result, as in this case, you can do:: +The other thing is that the result of calling ``.getall()`` is a list: it is +possible that a selector returns more than one result, so we extract them all. +When you know you just want the first result, as in this case, you can do:: >>> response.css('title::text').get() 'Quotes to Scrape' @@ -392,10 +392,10 @@ using the ``quote`` object we just created:: >>> author 'Albert Einstein' -Given that the tags are a list of strings, we can use the ``.extract()`` method +Given that the tags are a list of strings, we can use the ``.getall()`` method to get all of them:: - >>> tags = quote.css("div.tags a.tag::text").extract() + >>> tags = quote.css("div.tags a.tag::text").getall() >>> tags ['change', 'deep-thoughts', 'thinking', 'world'] @@ -405,7 +405,7 @@ quotes elements and put them together into a Python dictionary:: >>> for quote in response.css("div.quote"): ... text = quote.css("span.text::text").get() ... author = quote.css("small.author::text").get() - ... tags = quote.css("div.tags a.tag::text").extract() + ... tags = quote.css("div.tags a.tag::text").getall() ... print(dict(text=text, author=author, tags=tags)) {'tags': ['change', 'deep-thoughts', 'thinking', 'world'], 'author': 'Albert Einstein', 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'} {'tags': ['abilities', 'choices'], 'author': 'J.K. Rowling', 'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”'} @@ -438,7 +438,7 @@ in the callback, as you can see below:: yield { 'text': quote.css('span.text::text').get(), 'author': quote.css('small.author::text').get(), - 'tags': quote.css('div.tags a.tag::text').extract(), + 'tags': quote.css('div.tags a.tag::text').getall(), } If you run this spider, it will output the extracted data with the log:: @@ -543,7 +543,7 @@ page, extracting data from it:: yield { 'text': quote.css('span.text::text').get(), 'author': quote.css('small.author::text').get(), - 'tags': quote.css('div.tags a.tag::text').extract(), + 'tags': quote.css('div.tags a.tag::text').getall(), } next_page = response.css('li.next a::attr(href)').get() @@ -594,7 +594,7 @@ As a shortcut for creating Request objects you can use yield { 'text': quote.css('span.text::text').get(), 'author': quote.css('span small::text').get(), - 'tags': quote.css('div.tags a.tag::text').extract(), + 'tags': quote.css('div.tags a.tag::text').getall(), } next_page = response.css('li.next a::attr(href)').get() diff --git a/docs/topics/shell.rst b/docs/topics/shell.rst index 9de6abef7..68a0b19b5 100644 --- a/docs/topics/shell.rst +++ b/docs/topics/shell.rst @@ -184,8 +184,8 @@ After that, we can start playing with the objects:: >>> fetch("https://reddit.com") - >>> response.xpath('//title/text()').extract() - ['reddit: the front page of the internet'] + >>> response.xpath('//title/text()').get() + 'reddit: the front page of the internet' >>> request = request.replace(method="POST") diff --git a/docs/topics/spiders.rst b/docs/topics/spiders.rst index 4505b7315..a08dc30f2 100644 --- a/docs/topics/spiders.rst +++ b/docs/topics/spiders.rst @@ -229,11 +229,11 @@ Return multiple Requests and items from a single callback:: ] def parse(self, response): - for h3 in response.xpath('//h3').extract(): + for h3 in response.xpath('//h3').getall(): yield {"title": h3} - for url in response.xpath('//a/@href').extract(): - yield scrapy.Request(url, callback=self.parse) + for href in response.xpath('//a/@href').getall(): + yield scrapy.Request(response.urljoin(href), self.parse) Instead of :attr:`~.start_urls` you can use :meth:`~.start_requests` directly; to give data more structure you can use :ref:`topics-items`:: @@ -251,11 +251,11 @@ to give data more structure you can use :ref:`topics-items`:: yield scrapy.Request('http://www.example.com/3.html', self.parse) def parse(self, response): - for h3 in response.xpath('//h3').extract(): + for h3 in response.xpath('//h3').getall(): yield MyItem(title=h3) - for url in response.xpath('//a/@href').extract(): - yield scrapy.Request(url, callback=self.parse) + for href in response.xpath('//a/@href').getall(): + yield scrapy.Request(response.urljoin(href), self.parse) .. _spiderargs: @@ -545,7 +545,7 @@ These spiders are pretty easy to use, let's have a look at one example:: itertag = 'item' def parse_node(self, response, node): - self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.extract())) + self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.getall())) item = TestItem() item['id'] = node.xpath('@id').get() From 7fdfdb7fa244ed44274aaa9750efec1c333eedd1 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Wed, 22 Aug 2018 17:34:44 +0500 Subject: [PATCH 75/79] DOC reorganize selectors tutorial, port more topics from parsel docs, adjust wording in the introduction --- docs/topics/selectors.rst | 514 +++++++++++++++++++++----------------- 1 file changed, 281 insertions(+), 233 deletions(-) diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index cf1b0ba22..2db982b37 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -25,17 +25,14 @@ either by `XPath`_ or `CSS`_ expressions. used with HTML. `CSS`_ is a language for applying styles to HTML documents. It defines selectors to associate those styles with specific HTML elements. -Scrapy selectors are powered by `parsel`_ library, which uses `lxml`_ library -under the hood. It means Scrapy selectors are very similar in speed and -parsing accuracy to lxml. +.. note:: + Scrapy Selectors is a thin wrapper around `parsel`_ library; the purpose of + this wrapper is to provide better integration with Scrapy Response objects. -This page explains how selectors work and describes their API which is very -small and simple, unlike the `lxml`_ API which is much bigger because the -`lxml`_ library can be used for many other tasks, besides selecting markup -documents. - -For a complete reference of the selectors API see -:ref:`Selector reference ` + `parsel`_ is a stand-alone web scraping library which can be used without + Scrapy. It uses `lxml`_ library under the hood, and implements an + easy API on top of lxml API. It means Scrapy selectors are very similar + in speed and parsing accuracy to lxml. .. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/ .. _lxml: http://lxml.de/ @@ -73,9 +70,8 @@ Constructing from response:: >>> Selector(response=response).xpath('//span/text()').get() 'good' -For convenience, response objects expose a selector on `.selector` attribute, -it's totally OK to use this shortcut when possible. By using it you can -ensure the response body is parsed only once:: +For convenience, response objects expose a selector on `.selector` attribute. +By using it you can ensure the response body is parsed only once:: >>> response.selector.xpath('//span/text()').get() 'good' @@ -88,8 +84,10 @@ more shortcuts: ``response.xpath()`` and ``response.css()``:: >>> response.css('span::text').get() 'good' -Usually there is no need to construct Scrapy selectors manually because of -these shortcuts. +Usually there is no need to construct Scrapy selectors manually: +``response`` object is available in Spider callbacks, so in most cases +it is more convenient to use ``response.css()`` and ``response.xpath()`` +shortcuts. Using selectors --------------- @@ -392,6 +390,71 @@ Use it to extract just the first matching string:: >>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)') 'My image 1' +.. _old-extraction-api: + +extract() and extract_first() +----------------------------- + +If you're a long-time Scrapy user, you're probably familiar +with ``.extract()`` and ``.extract_first()`` selector methods. Many blog posts +and tutorials are using them as well. These methods are still supported +by Scrapy, there are **no plans** to deprecate them. + +However, Scrapy usage docs are now written using ``.get()`` and +``.getall()`` methods. We feel that these new methods result in a more concise +and readable code. + +The following examples show how these methods map to each other. + +1. ``SelectorList.get()`` is the same as ``SelectorList.extract_first()``:: + + >>> response.css('a::attr(href)').get() + 'image1.html' + >>> response.css('a::attr(href)').extract_first() + 'image1.html' + +2. ``SelectorList.getall()`` is the same as ``SelectorList.extract()``:: + + >>> response.css('a::attr(href)').getall() + ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] + >>> response.css('a::attr(href)').extract() + ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] + +2. ``Selector.get()`` is the same as ``Selector.extract()``:: + + >>> response.css('a::attr(href)')[0].get() + 'image1.html' + >>> response.css('a::attr(href)')[0].extract() + 'image1.html' + +4. For consistency, there is also ``Selector.getall()``, which returns a list:: + + >>> response.css('a::attr(href)')[0].getall() + ['image1.html'] + +So, the main difference is that output of ``.get()`` and ``.getall()`` methods +is more predictable: ``.get()`` always returns a single result, ``.getall()`` +always returns a list of all extracted results. With ``.extract()`` method +it was not always obvious if a result is a list or not; to get a single +result either ``.extract()`` or ``.extract_first()`` should be called. + + +.. _topics-selectors-xpaths: + +Working with XPaths +=================== + +Here are some tips which may help you to use XPath with Scrapy selectors +effectively. If you are not much familiar with XPath yet, +you may want to take a look first at this `XPath tutorial`_. + +.. note:: + Some of the tips are based on `this post from ScrapingHub's blog`_. + +.. _`XPath tutorial`: http://www.zvon.org/comp/r/tut-XPath_1.html +.. _`this post from ScrapingHub's blog`: https://blog.scrapinghub.com/2014/07/17/xpath-tips-from-the-web-scraping-trenches/ + + .. _topics-selectors-relative-xpaths: Working with relative XPaths @@ -428,6 +491,114 @@ XPath specification. .. _Location Paths: https://www.w3.org/TR/xpath#location-paths +When querying by class, consider using CSS +------------------------------------------ + +Because an element can contain multiple CSS classes, the XPath way to select elements +by class is the rather verbose:: + + *[contains(concat(' ', normalize-space(@class), ' '), ' someclass ')] + +If you use ``@class='someclass'`` you may end up missing elements that have +other classes, and if you just use ``contains(@class, 'someclass')`` to make up +for that you may end up with more elements that you want, if they have a different +class name that shares the string ``someclass``. + +As it turns out, Scrapy selectors allow you to chain selectors, so most of the time +you can just select by class using CSS and then switch to XPath when needed:: + + >>> from scrapy import Selector + >>> sel = Selector(text='
        ') + >>> sel.css('.shout').xpath('./time/@datetime').getall() + ['2014-07-23 19:00'] + +This is cleaner than using the verbose XPath trick shown above. Just remember +to use the ``.`` in the XPath expressions that will follow. + +Beware of the difference between //node[1] and (//node)[1] +---------------------------------------------------------- + +``//node[1]`` selects all the nodes occurring first under their respective parents. + +``(//node)[1]`` selects all the nodes in the document, and then gets only the first of them. + +Example:: + + >>> from scrapy import Selector + >>> sel = Selector(text=""" + ....:
          + ....:
        • 1
        • + ....:
        • 2
        • + ....:
        • 3
        • + ....:
        + ....:
          + ....:
        • 4
        • + ....:
        • 5
        • + ....:
        • 6
        • + ....:
        """) + >>> xp = lambda x: sel.xpath(x).getall() + +This gets all first ``
      • `` elements under whatever it is its parent:: + + >>> xp("//li[1]") + ['
      • 1
      • ', '
      • 4
      • '] + +And this gets the first ``
      • `` element in the whole document:: + + >>> xp("(//li)[1]") + ['
      • 1
      • '] + +This gets all first ``
      • `` elements under an ``
          `` parent:: + + >>> xp("//ul/li[1]") + ['
        • 1
        • ', '
        • 4
        • '] + +And this gets the first ``
        • `` element under an ``
            `` parent in the whole document:: + + >>> xp("(//ul/li)[1]") + ['
          • 1
          • '] + +Using text nodes in a condition +------------------------------- + +When you need to use the text content as argument to an `XPath string function`_, +avoid using ``.//text()`` and use just ``.`` instead. + +This is because the expression ``.//text()`` yields a collection of text elements -- a *node-set*. +And when a node-set is converted to a string, which happens when it is passed as argument to +a string function like ``contains()`` or ``starts-with()``, it results in the text for the first element only. + +Example:: + + >>> from scrapy import Selector + >>> sel = Selector(text='Click here to go to the Next Page') + +Converting a *node-set* to string:: + + >>> sel.xpath('//a//text()').getall() # take a peek at the node-set + ['Click here to go to the ', 'Next Page'] + >>> sel.xpath("string(//a[1]//text())").getall() # convert it to string + ['Click here to go to the '] + +A *node* converted to a string, however, puts together the text of itself plus of all its descendants:: + + >>> sel.xpath("//a[1]").getall() # select the first node + ['Click here to go to the Next Page'] + >>> sel.xpath("string(//a[1])").getall() # convert it to string + ['Click here to go to the Next Page'] + +So, using the ``.//text()`` node-set won't select anything in this case:: + + >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").getall() + [] + +But using the ``.`` to mean the node, works:: + + >>> sel.xpath("//a[contains(., 'Next Page')]").getall() + ['Click here to go to the Next Page'] + +.. _`XPath string function`: https://www.w3.org/TR/xpath/#section-String-Functions + .. _topics-selectors-xpath-variables: Variables in XPath expressions @@ -461,6 +632,69 @@ on `XPath variables`_. .. _XPath variables: https://parsel.readthedocs.io/en/latest/usage.html#variables-in-xpath-expressions + +.. _removing-namespaces: + +Removing namespaces +------------------- + +When dealing with scraping projects, it is often quite convenient to get rid of +namespaces altogether and just work with element names, to write more +simple/convenient XPaths. You can use the +:meth:`Selector.remove_namespaces` method for that. + +Let's show an example that illustrates this with GitHub blog atom feed. + +.. highlight:: sh + +First, we open the shell with the url we want to scrape:: + + $ scrapy shell https://github.com/blog.atom + +.. highlight:: xml + +This is how the file starts:: + + + + tag:github.com,2008:/blog + ... + +You can see two namespace declarations: a default "http://www.w3.org/2005/Atom" +and another one using the "media:" prefix for "http://search.yahoo.com/mrss/". + +.. highlight:: python + +Once in the shell we can try selecting all ```` objects and see that it +doesn't work (because the Atom XML namespace is obfuscating those nodes):: + + >>> response.xpath("//link") + [] + +But once we call the :meth:`Selector.remove_namespaces` method, all +nodes can be accessed directly by their names:: + + >>> response.selector.remove_namespaces() + >>> response.xpath("//link") + [, + , + ... + +If you wonder why the namespace removal procedure isn't always called by default +instead of having to call it manually, this is because of two reasons, which, in order +of relevance, are: + +1. Removing namespaces requires to iterate and modify all nodes in the + document, which is a reasonably expensive operation to perform by default + for all documents crawled by Scrapy + +2. There could be some cases where using namespaces is actually required, in + case some element names clash between namespaces. These cases are very rare + though. + + Using EXSLT extensions ---------------------- @@ -606,174 +840,44 @@ inside another ``itemscope``. .. _regular expressions: http://exslt.org/regexp/index.html .. _set manipulation: http://exslt.org/set/index.html +Other XPath extensions +---------------------- -Some XPath tips ---------------- +Scrapy selectors also provide a sorely missed XPath extension function +``has-class`` that returns ``True`` for nodes that have all of the specified +HTML classes. -Here are some tips that you may find useful when using XPath -with Scrapy selectors, based on `this post from ScrapingHub's blog`_. -If you are not much familiar with XPath yet, -you may want to take a look first at this `XPath tutorial`_. +.. highlight:: html +For the following HTML:: -.. _`XPath tutorial`: http://www.zvon.org/comp/r/tut-XPath_1.html -.. _`this post from ScrapingHub's blog`: https://blog.scrapinghub.com/2014/07/17/xpath-tips-from-the-web-scraping-trenches/ +

            First

            +

            Second

            +

            Third

            +

            Fourth

            +.. highlight:: python -Using text nodes in a condition -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You can use it like this:: -When you need to use the text content as argument to an `XPath string function`_, -avoid using ``.//text()`` and use just ``.`` instead. - -This is because the expression ``.//text()`` yields a collection of text elements -- a *node-set*. -And when a node-set is converted to a string, which happens when it is passed as argument to -a string function like ``contains()`` or ``starts-with()``, it results in the text for the first element only. - -Example:: - - >>> from scrapy import Selector - >>> sel = Selector(text='Click here to go to the Next Page') - -Converting a *node-set* to string:: - - >>> sel.xpath('//a//text()').getall() # take a peek at the node-set - ['Click here to go to the ', 'Next Page'] - >>> sel.xpath("string(//a[1]//text())").getall() # convert it to string - ['Click here to go to the '] - -A *node* converted to a string, however, puts together the text of itself plus of all its descendants:: - - >>> sel.xpath("//a[1]").getall() # select the first node - ['Click here to go to the Next Page'] - >>> sel.xpath("string(//a[1])").getall() # convert it to string - ['Click here to go to the Next Page'] - -So, using the ``.//text()`` node-set won't select anything in this case:: - - >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").getall() + >>> response.xpath('//p[has-class("foo")]') + [, + ] + >>> response.xpath('//p[has-class("foo", "bar-baz")]') + [] + >>> response.xpath('//p[has-class("foo", "bar")]') [] -But using the ``.`` to mean the node, works:: +So XPath ``//p[has-class("foo", "bar-baz")]`` is roughly equivalent to CSS +``p.foo.bar-baz``. Please note, that it is slower in most of the cases, +because it's a pure-Python function that's invoked for every node in question +whereas the CSS lookup is translated into XPath and thus runs more efficiently, +so performance-wise its uses are limited to situations that are not easily +described with CSS selectors. - >>> sel.xpath("//a[contains(., 'Next Page')]").getall() - ['Click here to go to the Next Page'] +Parsel also simplifies adding your own XPath extensions. -.. _`XPath string function`: https://www.w3.org/TR/xpath/#section-String-Functions - -Beware of the difference between //node[1] and (//node)[1] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``//node[1]`` selects all the nodes occurring first under their respective parents. - -``(//node)[1]`` selects all the nodes in the document, and then gets only the first of them. - -Example:: - - >>> from scrapy import Selector - >>> sel = Selector(text=""" - ....:
              - ....:
            • 1
            • - ....:
            • 2
            • - ....:
            • 3
            • - ....:
            - ....:
              - ....:
            • 4
            • - ....:
            • 5
            • - ....:
            • 6
            • - ....:
            """) - >>> xp = lambda x: sel.xpath(x).getall() - -This gets all first ``
          • `` elements under whatever it is its parent:: - - >>> xp("//li[1]") - ['
          • 1
          • ', '
          • 4
          • '] - -And this gets the first ``
          • `` element in the whole document:: - - >>> xp("(//li)[1]") - ['
          • 1
          • '] - -This gets all first ``
          • `` elements under an ``
              `` parent:: - - >>> xp("//ul/li[1]") - ['
            • 1
            • ', '
            • 4
            • '] - -And this gets the first ``
            • `` element under an ``
                `` parent in the whole document:: - - >>> xp("(//ul/li)[1]") - ['
              • 1
              • '] - -When querying by class, consider using CSS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Because an element can contain multiple CSS classes, the XPath way to select elements -by class is the rather verbose:: - - *[contains(concat(' ', normalize-space(@class), ' '), ' someclass ')] - -If you use ``@class='someclass'`` you may end up missing elements that have -other classes, and if you just use ``contains(@class, 'someclass')`` to make up -for that you may end up with more elements that you want, if they have a different -class name that shares the string ``someclass``. - -As it turns out, Scrapy selectors allow you to chain selectors, so most of the time -you can just select by class using CSS and then switch to XPath when needed:: - - >>> from scrapy import Selector - >>> sel = Selector(text='
                ') - >>> sel.css('.shout').xpath('./time/@datetime').getall() - ['2014-07-23 19:00'] - -This is cleaner than using the verbose XPath trick shown above. Just remember -to use the ``.`` in the XPath expressions that will follow. - -.. _old-extraction-api: - -extract() and extract_first() ------------------------------ - -If you're a long-time Scrapy user, you're probably familiar -with ``.extract()`` and ``.extract_first()`` selector methods. These methods -are still supported by Scrapy, there are no plans to deprecate them. - -However, Scrapy usage docs are now written using ``.get()`` and -``.getall()`` methods. We feel that these new methods result in a more concise -and readable code. - -The following examples show how these methods map to each other. - -1. ``SelectorList.get()`` is the same as ``SelectorList.extract_first()``:: - - >>> response.css('a::attr(href)').get() - 'image1.html' - >>> response.css('a::attr(href)').extract_first() - 'image1.html' - -2. ``SelectorList.getall()`` is the same as ``SelectorList.extract()``:: - - >>> response.css('a::attr(href)').getall() - ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] - >>> response.css('a::attr(href)').extract() - ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] - -2. ``Selector.get()`` is the same as ``Selector.extract()``:: - - >>> response.css('a::attr(href)')[0].get() - 'image1.html' - >>> response.css('a::attr(href)')[0].extract() - 'image1.html' - -4. For consistency, there is also ``Selector.getall()``, which returns a list:: - - >>> response.css('a::attr(href)')[0].getall() - ['image1.html'] - -So, the main difference is that output of ``.get()`` and ``.getall()`` methods -is more predictable: ``.get()`` always returns a single result, ``.getall()`` -always returns a list of all extracted results. With ``.extract()`` method -it was not always obvious if a result is a list or not; to get a single -result either ``.extract()`` or ``.extract_first()`` should be called. +.. autofunction:: parsel.xpathfuncs.set_xpathfunc .. _topics-selectors-ref: @@ -909,6 +1013,11 @@ SelectorList objects their results flattened, as a list of unicode strings. +.. _selector-examples: + +Examples +======== + .. _selector-examples-html: Selector examples on HTML response @@ -958,65 +1067,4 @@ instantiated with an :class:`~scrapy.http.XmlResponse` object:: sel.register_namespace("g", "http://base.google.com/ns/1.0") sel.xpath("//g:price").getall() -.. _removing-namespaces: - -Removing namespaces -------------------- - -When dealing with scraping projects, it is often quite convenient to get rid of -namespaces altogether and just work with element names, to write more -simple/convenient XPaths. You can use the -:meth:`Selector.remove_namespaces` method for that. - -Let's show an example that illustrates this with GitHub blog atom feed. - -.. highlight:: sh - -First, we open the shell with the url we want to scrape:: - - $ scrapy shell https://github.com/blog.atom - -.. highlight:: xml - -This is how the file starts:: - - - - tag:github.com,2008:/blog - ... - -You can see two namespace declarations: a default "http://www.w3.org/2005/Atom" -and another one using the "media:" prefix for "http://search.yahoo.com/mrss/". - -.. highlight:: python - -Once in the shell we can try selecting all ```` objects and see that it -doesn't work (because the Atom XML namespace is obfuscating those nodes):: - - >>> response.xpath("//link") - [] - -But once we call the :meth:`Selector.remove_namespaces` method, all -nodes can be accessed directly by their names:: - - >>> response.selector.remove_namespaces() - >>> response.xpath("//link") - [, - , - ... - -If you wonder why the namespace removal procedure isn't always called by default -instead of having to call it manually, this is because of two reasons, which, in order -of relevance, are: - -1. Removing namespaces requires to iterate and modify all nodes in the - document, which is a reasonably expensive operation to perform by default - for all documents crawled by Scrapy - -2. There could be some cases where using namespaces is actually required, in - case some element names clash between namespaces. These cases are very rare - though. - .. _Google Base XML feed: https://support.google.com/merchants/answer/160589?hl=en&ref_topic=2473799 From dc95ecbe25a5921f902fbccc93d50f7682cefda2 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Wed, 12 Sep 2018 18:36:25 +0500 Subject: [PATCH 76/79] DOC use autodocs for selectors; document more methods and attributes; suggest get/getall --- docs/topics/selectors.rst | 114 +++++++++---------------------------- scrapy/selector/unified.py | 33 +++++++++++ 2 files changed, 60 insertions(+), 87 deletions(-) diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 2db982b37..95d6a1a04 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -891,127 +891,67 @@ Built-in Selectors reference Selector objects ---------------- -.. class:: Selector(response=None, text=None, type=None) +.. autoclass:: Selector - An instance of :class:`Selector` is a wrapper over response to select - certain parts of its content. - - ``response`` is an :class:`~scrapy.http.HtmlResponse` or an - :class:`~scrapy.http.XmlResponse` object that will be used for selecting and - extracting data. - - ``text`` is a unicode string or utf-8 encoded text for cases when a - ``response`` isn't available. Using ``text`` and ``response`` together is - undefined behavior. - - ``type`` defines the selector type, it can be ``"html"``, ``"xml"`` or ``None`` (default). - - If ``type`` is ``None``, the selector automatically chooses the best type - based on ``response`` type (see below), or defaults to ``"html"`` in case it - is used together with ``text``. - - If ``type`` is ``None`` and a ``response`` is passed, the selector type is - inferred from the response type as follows: - - * ``"html"`` for :class:`~scrapy.http.HtmlResponse` type - * ``"xml"`` for :class:`~scrapy.http.XmlResponse` type - * ``"html"`` for anything else - - Otherwise, if ``type`` is set, the selector type will be forced and no - detection will occur. - - .. method:: xpath(query) - - Find nodes matching the xpath ``query`` and return the result as a - :class:`SelectorList` instance with all elements flattened. List - elements implement :class:`Selector` interface too. - - ``query`` is a string containing the XPATH query to apply. + .. automethod:: xpath .. note:: For convenience, this method can be called as ``response.xpath()`` - .. method:: css(query) - - Apply the given CSS selector and return a :class:`SelectorList` instance. - - ``query`` is a string containing the CSS selector to apply. - - In the background, CSS queries are translated into XPath queries using - `cssselect`_ library and run ``.xpath()`` method. + .. automethod:: css .. note:: - For convenience this method can be called as ``response.css()`` + For convenience, this method can be called as ``response.css()`` - .. method:: extract() + .. automethod:: get - Serialize and return the matched nodes as a list of unicode strings. - Percent encoded content is unquoted. + See also: :ref:`old-extraction-api` - .. method:: re(regex) + .. autoattribute:: attrib - Apply the given regex and return a list of unicode strings with the - matches. + See also: :ref:`selecting-attributes`. - ``regex`` can be either a compiled regular expression or a string which - will be compiled to a regular expression using ``re.compile(regex)`` + .. automethod:: re - .. note:: + .. automethod:: re_first - Note that ``re()`` and ``re_first()`` both decode HTML entities (except ``<`` and ``&``). + .. automethod:: register_namespace - .. method:: register_namespace(prefix, uri) + .. automethod:: remove_namespaces - Register the given namespace to be used in this :class:`Selector`. - Without registering namespaces you can't select or extract data from - non-standard namespaces. See examples below. + .. automethod:: __bool__ - .. method:: remove_namespaces() - - Remove all namespaces, allowing to traverse the document using - namespace-less xpaths. See example below. - - .. method:: __nonzero__() - - Returns ``True`` if there is any real content selected or ``False`` - otherwise. In other words, the boolean value of a :class:`Selector` is - given by the contents it selects. + .. automethod:: getall + This method is added to Selector for consistency; it is more useful + with SelectorList. See also: :ref:`old-extraction-api` SelectorList objects -------------------- -.. class:: SelectorList +.. autoclass:: SelectorList - The :class:`SelectorList` class is a subclass of the builtin ``list`` - class, which provides a few additional methods. + .. automethod:: xpath - .. method:: xpath(query) + .. automethod:: css - Call the ``.xpath()`` method for each element in this list and return - their results flattened as another :class:`SelectorList`. + .. automethod:: getall - ``query`` is the same argument as the one in :meth:`Selector.xpath` + See also: :ref:`old-extraction-api` - .. method:: css(query) + .. automethod:: get - Call the ``.css()`` method for each element in this list and return - their results flattened as another :class:`SelectorList`. + See also: :ref:`old-extraction-api` - ``query`` is the same argument as the one in :meth:`Selector.css` + .. automethod:: re - .. method:: extract() + .. automethod:: re_first - Call the ``.extract()`` method for each element in this list and return - their results flattened, as a list of unicode strings. - - .. method:: re() - - Call the ``.re()`` method for each element in this list and return - their results flattened, as a list of unicode strings. + .. autoattribute:: attrib + See also: :ref:`selecting-attributes`. .. _selector-examples: diff --git a/scrapy/selector/unified.py b/scrapy/selector/unified.py index 64cb0232c..8f6cb1d79 100644 --- a/scrapy/selector/unified.py +++ b/scrapy/selector/unified.py @@ -27,6 +27,10 @@ def _response_from_text(text, st): class SelectorList(_ParselSelector.selectorlist_cls, object_ref): + """ + The :class:`SelectorList` class is a subclass of the builtin ``list`` + class, which provides a few additional methods. + """ @deprecated(use_instead='.extract()') def extract_unquoted(self): return [x.extract_unquoted() for x in self] @@ -41,6 +45,35 @@ class SelectorList(_ParselSelector.selectorlist_cls, object_ref): class Selector(_ParselSelector, object_ref): + """ + An instance of :class:`Selector` is a wrapper over response to select + certain parts of its content. + + ``response`` is an :class:`~scrapy.http.HtmlResponse` or an + :class:`~scrapy.http.XmlResponse` object that will be used for selecting + and extracting data. + + ``text`` is a unicode string or utf-8 encoded text for cases when a + ``response`` isn't available. Using ``text`` and ``response`` together is + undefined behavior. + + ``type`` defines the selector type, it can be ``"html"``, ``"xml"`` + or ``None`` (default). + + If ``type`` is ``None``, the selector automatically chooses the best type + based on ``response`` type (see below), or defaults to ``"html"`` in case it + is used together with ``text``. + + If ``type`` is ``None`` and a ``response`` is passed, the selector type is + inferred from the response type as follows: + + * ``"html"`` for :class:`~scrapy.http.HtmlResponse` type + * ``"xml"`` for :class:`~scrapy.http.XmlResponse` type + * ``"html"`` for anything else + + Otherwise, if ``type`` is set, the selector type will be forced and no + detection will occur. + """ __slots__ = ['response'] selectorlist_cls = SelectorList From 9db21e55028feec932136e74dad0c0f0dbe7f436 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Sat, 15 Sep 2018 02:43:37 +0500 Subject: [PATCH 77/79] DOC fix remove_namespaces example See https://github.com/scrapy/parsel/pull/119 --- docs/topics/selectors.rst | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 95d6a1a04..20c3fff3c 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -643,27 +643,30 @@ namespaces altogether and just work with element names, to write more simple/convenient XPaths. You can use the :meth:`Selector.remove_namespaces` method for that. -Let's show an example that illustrates this with GitHub blog atom feed. +Let's show an example that illustrates this with the Python Insider blog atom feed. .. highlight:: sh First, we open the shell with the url we want to scrape:: - $ scrapy shell https://github.com/blog.atom - -.. highlight:: xml + $ scrapy shell https://feeds.feedburner.com/PythonInsider This is how the file starts:: - - tag:github.com,2008:/blog + ... -You can see two namespace declarations: a default "http://www.w3.org/2005/Atom" -and another one using the "media:" prefix for "http://search.yahoo.com/mrss/". +You can see several namespace declarations including a default +"http://www.w3.org/2005/Atom" and another one using the "gd:" prefix for +"http://schemas.google.com/g/2005". .. highlight:: python @@ -678,8 +681,8 @@ nodes can be accessed directly by their names:: >>> response.selector.remove_namespaces() >>> response.xpath("//link") - [, - , + [, + , ... If you wonder why the namespace removal procedure isn't always called by default From 2c3b2158c99953823500f275ccc5206c11c9a811 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Tue, 18 Sep 2018 05:02:17 +0500 Subject: [PATCH 78/79] DOC address @stummjr's review comments * fixed several small issues * re-written "Creating Selectors" section * fixed remaining .extract usage in tests --- docs/intro/tutorial.rst | 6 ++-- docs/topics/selectors.rst | 52 +++++++++++++++++++---------------- tests/test_utils_iterators.py | 4 +-- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/docs/intro/tutorial.rst b/docs/intro/tutorial.rst index 46e84b21c..ad17ef096 100644 --- a/docs/intro/tutorial.rst +++ b/docs/intro/tutorial.rst @@ -285,9 +285,9 @@ There's a lesson here: for most scraping code, you want it to be resilient to errors due to things not being found on a page, so that even if some parts fail to be scraped, you can at least get **some** data. -Besides the :meth:`~scrapy.selector.Selector.extract` and +Besides the :meth:`~scrapy.selector.SelectorList.getall` and :meth:`~scrapy.selector.SelectorList.get` methods, you can also use -the :meth:`~scrapy.selector.Selector.re` method to extract using `regular +the :meth:`~scrapy.selector.SelectorList.re` method to extract using `regular expressions`_:: >>> response.css('title::text').re(r'Quotes.*') @@ -649,7 +649,7 @@ this time for scraping author information:: def parse_author(self, response): def extract_with_css(query): - return response.css(query).get().strip() + return response.css(query).get(default='').strip() yield { 'name': extract_with_css('h3.author-title::text'), diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 20c3fff3c..68913c697 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -50,28 +50,8 @@ Constructing selectors .. highlight:: python -Scrapy selectors are instances of :class:`~scrapy.selector.Selector` class -constructed by passing **text** or :class:`~scrapy.http.TextResponse` -object. It automatically chooses the best parsing rules (XML vs HTML) based on -input type:: - - >>> from scrapy.selector import Selector - >>> from scrapy.http import HtmlResponse - -Constructing from text:: - - >>> body = 'good' - >>> Selector(text=body).xpath('//span/text()').get() - 'good' - -Constructing from response:: - - >>> response = HtmlResponse(url='http://example.com', body=body) - >>> Selector(response=response).xpath('//span/text()').get() - 'good' - -For convenience, response objects expose a selector on `.selector` attribute. -By using it you can ensure the response body is parsed only once:: +Response objects expose a :class:`~scrapy.selector.Selector` instance +on ``.selector`` attribute:: >>> response.selector.xpath('//span/text()').get() 'good' @@ -84,10 +64,34 @@ more shortcuts: ``response.xpath()`` and ``response.css()``:: >>> response.css('span::text').get() 'good' +Scrapy selectors are instances of :class:`~scrapy.selector.Selector` class +constructed by passing either :class:`~scrapy.http.TextResponse` object or +markup as an unicode string (in ``text`` argument). Usually there is no need to construct Scrapy selectors manually: ``response`` object is available in Spider callbacks, so in most cases it is more convenient to use ``response.css()`` and ``response.xpath()`` -shortcuts. +shortcuts. By using ``response.selector`` or one of these shortcuts +you can also ensure the response body is parsed only once. + +But if required, it is possible to use ``Selector`` directly. +Constructing from text:: + + >>> from scrapy.selector import Selector + >>> body = 'good' + >>> Selector(text=body).xpath('//span/text()').get() + 'good' + +Constructing from response - :class:`~scrapy.http.HtmlResponse` is one of +:class:`~scrapy.http.TextResponse` subclasses:: + + >>> from scrapy.selector import Selector + >>> from scrapy.http import HtmlResponse + >>> response = HtmlResponse(url='http://example.com', body=body) + >>> Selector(response=response).xpath('//span/text()').get() + 'good' + +``Selector`` automatically chooses the best parsing rules +(XML vs HTML) based on input type. Using selectors --------------- @@ -139,7 +143,7 @@ is returned. ``.getall()`` returns a list with all results. Notice that CSS selectors can select text or attribute nodes using CSS3 pseudo-elements:: - >>> selector.css('title::text').get() + >>> response.css('title::text').get() 'Example website' As you can see, ``.xpath()`` and ``.css()`` methods return a diff --git a/tests/test_utils_iterators.py b/tests/test_utils_iterators.py index 00eb78068..2d845697e 100644 --- a/tests/test_utils_iterators.py +++ b/tests/test_utils_iterators.py @@ -32,8 +32,8 @@ class XmliterTestCase(unittest.TestCase): for x in self.xmliter(response, 'product'): attrs.append(( x.attrib['id'], - x.xpath("name/text()").extract(), - x.xpath("./type/text()").extract())) + x.xpath("name/text()").getall(), + x.xpath("./type/text()").getall())) self.assertEqual(attrs, [('001', ['Name 1'], ['Type 1']), ('002', ['Name 2'], ['Type 2'])]) From ffbd33edac0367e9f975b9863e0c31e1c2b72ebc Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Tue, 18 Sep 2018 05:03:35 +0500 Subject: [PATCH 79/79] DOC mention gotcha with `foo::text` selector and empty `foo` elements also, move "Selecting attributes" reference closer to `a::atr(href)` example --- docs/topics/selectors.rst | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 68913c697..9dced7473 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -279,6 +279,19 @@ Examples: 'Name: My image 5 ', '\n '] +* ``foo::text`` returns no results if ``foo`` element exists, but contains + no text (i.e. text is empty):: + + >>> response.css('img::text').getall() + [] + + This means ``.css('foo::text').get()`` could return None even if an element + exists. Use ``default=''`` if you always want a string:: + + >>> response.css('img::text').get() + >>> response.css('img::text').get(default='') + '' + * ``a::attr(href)`` selects the *href* attribute value of descendant links:: >>> response.css('a::attr(href)').getall() @@ -288,15 +301,14 @@ Examples: 'image4.html', 'image5.html'] +.. note:: + See also: :ref:`selecting-attributes`. + .. note:: You cannot chain these pseudo-elements. But in practice it would not make much sense: text nodes do not have attributes, and attribute values are string values already and do not have children nodes. -.. note:: - See also: :ref:`selecting-attributes`. - - .. _CSS Selectors: https://www.w3.org/TR/css3-selectors/#selectors .. _topics-selectors-nesting-selectors: