{"action":"created","comment":{"url":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/comments/27718743","id":27718743,"diff_hunk":"@@ -0,0 +1,44 @@\n+# -*- coding: utf-8 -*-\n+from crawlfrontier.core.components import CanonicalSolver\n+\n+\n+class BasicCanonicalSolver(CanonicalSolver):\n+    \"\"\"\n+    Implements a simple CanonicalSolver taking always first URL from redirect chain, if there were redirects.\n+    It allows easily to avoid leaking of requests in CF (e.g. when request issued by\n+    :attr:`get_next_requests() <crawlfrontier.core.manager.FrontierManager.get_next_requests>` never matched in\n+    :attr:`page_crawled() <crawlfrontier.core.manager.FrontierManager.page_crawled>`) at the price of duplicating\n+    records in CF for pages having more than one URL or complex redirects chains.\n+    \"\"\"\n+    def frontier_start(self):\n+        pass\n+\n+    def frontier_stop(self):\n+        pass\n+\n+    def add_seeds(self, seeds):\n+        pass\n+\n+    def page_crawled(self, response, links):\n+        pass\n+\n+    def request_error(self, page, error):\n+        pass\n+\n+    def get_canonical_url(self, obj):\n+        canonical_url = obj.url\n+        canonical_fingerprint = obj.meta['fingerprint']\n+        canonical_domain_fingerprint = obj.meta['domain']['fingerprint'] if 'domain' in obj.meta else str()\n+        if 'redirect_urls' in obj.meta:\n+            redirect_urls = obj.meta['redirect_urls']\n+            redirect_fingerprints = obj.meta['redirect_fingerprints']\n+            redirect_domains = obj.meta['redirect_domains']","path":"crawlfrontier/contrib/canonicalsolvers/basic.py","position":35,"original_position":35,"commit_id":"177fea5237b468d44761d3f97d287b7fd688047c","original_commit_id":"177fea5237b468d44761d3f97d287b7fd688047c","user":{"login":"vshlapakov","id":448111,"avatar_url":"https://avatars.githubusercontent.com/u/448111?v=3","gravatar_id":"","url":"https://api.github.com/users/vshlapakov","html_url":"https://github.com/vshlapakov","followers_url":"https://api.github.com/users/vshlapakov/followers","following_url":"https://api.github.com/users/vshlapakov/following{/other_user}","gists_url":"https://api.github.com/users/vshlapakov/gists{/gist_id}","starred_url":"https://api.github.com/users/vshlapakov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vshlapakov/subscriptions","organizations_url":"https://api.github.com/users/vshlapakov/orgs","repos_url":"https://api.github.com/users/vshlapakov/repos","events_url":"https://api.github.com/users/vshlapakov/events{/privacy}","received_events_url":"https://api.github.com/users/vshlapakov/received_events","type":"User","site_admin":false},"body":"Why do we need to track redirect domains chain? What is the use of it?","created_at":"2015-04-03T07:32:56Z","updated_at":"2015-04-03T07:32:56Z","html_url":"https://github.com/scrapinghub/crawl-frontier/pull/23#discussion_r27718743","pull_request_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23","_links":{"self":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/comments/27718743"},"html":{"href":"https://github.com/scrapinghub/crawl-frontier/pull/23#discussion_r27718743"},"pull_request":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23"}}},"pull_request":{"url":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23","id":32043477,"html_url":"https://github.com/scrapinghub/crawl-frontier/pull/23","diff_url":"https://github.com/scrapinghub/crawl-frontier/pull/23.diff","patch_url":"https://github.com/scrapinghub/crawl-frontier/pull/23.patch","issue_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/issues/23","number":23,"state":"open","locked":false,"title":"[WIP] Canonical url solver architecture","user":{"login":"sibiryakov","id":9044124,"avatar_url":"https://avatars.githubusercontent.com/u/9044124?v=3","gravatar_id":"","url":"https://api.github.com/users/sibiryakov","html_url":"https://github.com/sibiryakov","followers_url":"https://api.github.com/users/sibiryakov/followers","following_url":"https://api.github.com/users/sibiryakov/following{/other_user}","gists_url":"https://api.github.com/users/sibiryakov/gists{/gist_id}","starred_url":"https://api.github.com/users/sibiryakov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sibiryakov/subscriptions","organizations_url":"https://api.github.com/users/sibiryakov/orgs","repos_url":"https://api.github.com/users/sibiryakov/repos","events_url":"https://api.github.com/users/sibiryakov/events{/privacy}","received_events_url":"https://api.github.com/users/sibiryakov/received_events","type":"User","site_admin":false},"body":"![unnamed](https://cloud.githubusercontent.com/assets/9044124/6853450/8648e1b8-d3eb-11e4-97d4-e11343ce6f58.png)\r\n\r\nEach document in the web is identified with it's own checksum. At the same time, each document can have few different URLs (even on different domains) and graph of nodes redirecting to it. If our crawler will find the same document starting from some of redirect nodes (URLr1) or using one of it's own URLs, will system detect that this document is already known? What URL we will assign to the document at the end? From my experience, chances that we will face with document URL ambiguity are quite big, since redirects are quite common, especially when dealing with URL shorteners.\r\n\r\nCurrently Crawl Frontier has no such capability, and it's architecture isn't reflecting this issue. This PR offers the solution in form of new type of backend, called CanonicalSolver. Main purpose for it is to provide any component in CF with canonical url of the document. Integration in crawl frontier internal pipeline allows to hook on all types of events in CF. CanonicalSolver can have it's own persistent storage with redirect graph, and use document content for identification purposes.","created_at":"2015-03-26T17:55:26Z","updated_at":"2015-04-03T07:32:56Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4fa57cba23cef35eb893606f04fa622ec5c86389","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23/commits","review_comments_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23/comments","review_comment_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/issues/23/comments","statuses_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/statuses/177fea5237b468d44761d3f97d287b7fd688047c","head":{"label":"sibiryakov:canonical-urls","ref":"canonical-urls","sha":"177fea5237b468d44761d3f97d287b7fd688047c","user":{"login":"sibiryakov","id":9044124,"avatar_url":"https://avatars.githubusercontent.com/u/9044124?v=3","gravatar_id":"","url":"https://api.github.com/users/sibiryakov","html_url":"https://github.com/sibiryakov","followers_url":"https://api.github.com/users/sibiryakov/followers","following_url":"https://api.github.com/users/sibiryakov/following{/other_user}","gists_url":"https://api.github.com/users/sibiryakov/gists{/gist_id}","starred_url":"https://api.github.com/users/sibiryakov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sibiryakov/subscriptions","organizations_url":"https://api.github.com/users/sibiryakov/orgs","repos_url":"https://api.github.com/users/sibiryakov/repos","events_url":"https://api.github.com/users/sibiryakov/events{/privacy}","received_events_url":"https://api.github.com/users/sibiryakov/received_events","type":"User","site_admin":false},"repo":{"id":30012023,"name":"crawl-frontier","full_name":"sibiryakov/crawl-frontier","owner":{"login":"sibiryakov","id":9044124,"avatar_url":"https://avatars.githubusercontent.com/u/9044124?v=3","gravatar_id":"","url":"https://api.github.com/users/sibiryakov","html_url":"https://github.com/sibiryakov","followers_url":"https://api.github.com/users/sibiryakov/followers","following_url":"https://api.github.com/users/sibiryakov/following{/other_user}","gists_url":"https://api.github.com/users/sibiryakov/gists{/gist_id}","starred_url":"https://api.github.com/users/sibiryakov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sibiryakov/subscriptions","organizations_url":"https://api.github.com/users/sibiryakov/orgs","repos_url":"https://api.github.com/users/sibiryakov/repos","events_url":"https://api.github.com/users/sibiryakov/events{/privacy}","received_events_url":"https://api.github.com/users/sibiryakov/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sibiryakov/crawl-frontier","description":"A flexible frontier for web crawlers","fork":true,"url":"https://api.github.com/repos/sibiryakov/crawl-frontier","forks_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/forks","keys_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/teams","hooks_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/hooks","issue_events_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/issues/events{/number}","events_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/events","assignees_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/assignees{/user}","branches_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/branches{/branch}","tags_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/tags","blobs_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/git/refs{/sha}","trees_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/statuses/{sha}","languages_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/languages","stargazers_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/stargazers","contributors_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/contributors","subscribers_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/subscribers","subscription_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/subscription","commits_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/commits{/sha}","git_commits_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/git/commits{/sha}","comments_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/comments{/number}","issue_comment_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/issues/comments{/number}","contents_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/contents/{+path}","compare_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/merges","archive_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/downloads","issues_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/issues{/number}","pulls_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/pulls{/number}","milestones_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/milestones{/number}","notifications_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/labels{/name}","releases_url":"https://api.github.com/repos/sibiryakov/crawl-frontier/releases{/id}","created_at":"2015-01-29T09:00:22Z","updated_at":"2015-03-18T09:39:16Z","pushed_at":"2015-04-02T10:51:18Z","git_url":"git://github.com/sibiryakov/crawl-frontier.git","ssh_url":"git@github.com:sibiryakov/crawl-frontier.git","clone_url":"https://github.com/sibiryakov/crawl-frontier.git","svn_url":"https://github.com/sibiryakov/crawl-frontier","homepage":null,"size":3180,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"scrapinghub:development","ref":"development","sha":"e65e349d9ca76838fda7d8cea53f78f0d935bb24","user":{"login":"scrapinghub","id":699596,"avatar_url":"https://avatars.githubusercontent.com/u/699596?v=3","gravatar_id":"","url":"https://api.github.com/users/scrapinghub","html_url":"https://github.com/scrapinghub","followers_url":"https://api.github.com/users/scrapinghub/followers","following_url":"https://api.github.com/users/scrapinghub/following{/other_user}","gists_url":"https://api.github.com/users/scrapinghub/gists{/gist_id}","starred_url":"https://api.github.com/users/scrapinghub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scrapinghub/subscriptions","organizations_url":"https://api.github.com/users/scrapinghub/orgs","repos_url":"https://api.github.com/users/scrapinghub/repos","events_url":"https://api.github.com/users/scrapinghub/events{/privacy}","received_events_url":"https://api.github.com/users/scrapinghub/received_events","type":"Organization","site_admin":false},"repo":{"id":27003377,"name":"crawl-frontier","full_name":"scrapinghub/crawl-frontier","owner":{"login":"scrapinghub","id":699596,"avatar_url":"https://avatars.githubusercontent.com/u/699596?v=3","gravatar_id":"","url":"https://api.github.com/users/scrapinghub","html_url":"https://github.com/scrapinghub","followers_url":"https://api.github.com/users/scrapinghub/followers","following_url":"https://api.github.com/users/scrapinghub/following{/other_user}","gists_url":"https://api.github.com/users/scrapinghub/gists{/gist_id}","starred_url":"https://api.github.com/users/scrapinghub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scrapinghub/subscriptions","organizations_url":"https://api.github.com/users/scrapinghub/orgs","repos_url":"https://api.github.com/users/scrapinghub/repos","events_url":"https://api.github.com/users/scrapinghub/events{/privacy}","received_events_url":"https://api.github.com/users/scrapinghub/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scrapinghub/crawl-frontier","description":"A flexible frontier for web crawlers","fork":false,"url":"https://api.github.com/repos/scrapinghub/crawl-frontier","forks_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/forks","keys_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/teams","hooks_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/hooks","issue_events_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/issues/events{/number}","events_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/events","assignees_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/assignees{/user}","branches_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/branches{/branch}","tags_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/tags","blobs_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/git/refs{/sha}","trees_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/statuses/{sha}","languages_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/languages","stargazers_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/stargazers","contributors_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/contributors","subscribers_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/subscribers","subscription_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/subscription","commits_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/commits{/sha}","git_commits_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/git/commits{/sha}","comments_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/comments{/number}","issue_comment_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/issues/comments{/number}","contents_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/contents/{+path}","compare_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/merges","archive_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/downloads","issues_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/issues{/number}","pulls_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls{/number}","milestones_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/milestones{/number}","notifications_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/labels{/name}","releases_url":"https://api.github.com/repos/scrapinghub/crawl-frontier/releases{/id}","created_at":"2014-11-22T15:42:50Z","updated_at":"2015-03-25T00:05:56Z","pushed_at":"2015-04-02T22:07:37Z","git_url":"git://github.com/scrapinghub/crawl-frontier.git","ssh_url":"git@github.com:scrapinghub/crawl-frontier.git","clone_url":"https://github.com/scrapinghub/crawl-frontier.git","svn_url":"https://github.com/scrapinghub/crawl-frontier","homepage":null,"size":4191,"stargazers_count":24,"watchers_count":24,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":12,"mirror_url":null,"open_issues_count":7,"forks":12,"open_issues":7,"watchers":24,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23"},"html":{"href":"https://github.com/scrapinghub/crawl-frontier/pull/23"},"issue":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/issues/23"},"comments":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/issues/23/comments"},"review_comments":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23/comments"},"review_comment":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/pulls/23/commits"},"statuses":{"href":"https://api.github.com/repos/scrapinghub/crawl-frontier/statuses/177fea5237b468d44761d3f97d287b7fd688047c"}}}}