From 6f1b122b6ff1553f8d50a1dc2de3cf23e33b9472 Mon Sep 17 00:00:00 2001 From: jsclose <jsclose@umich.edu> Date: Wed, 21 Feb 2018 19:50:51 -0500 Subject: [PATCH] working on crawler buffer --- crawler/SocketReader.cpp | 17 +++++++++----- crawler/spider.cpp | 31 +++++++++++++++---------- docMap.txt | Bin 1250 -> 3423 bytes robots/en.wikipedia.org.txt | 35 +++++++++++++++++++++++++++++ robots/www.bbc.com.txt | 40 +++++++++++++++++++++++++++++++++ robots/www.dailymail.co.uk.txt | 15 +++++++++++++ robots/www.nytimes.com.txt | 16 +++++++++++++ shared/urlTest.cpp | 21 ++++++++++++----- tests/webSeed.txt | 4 +++- url_test | Bin 23676 -> 23580 bytes 10 files changed, 154 insertions(+), 25 deletions(-) create mode 100755 robots/en.wikipedia.org.txt create mode 100755 robots/www.bbc.com.txt create mode 100755 robots/www.dailymail.co.uk.txt create mode 100755 robots/www.nytimes.com.txt diff --git a/crawler/SocketReader.cpp b/crawler/SocketReader.cpp index 655fc2b..ee6e7b7 100644 --- a/crawler/SocketReader.cpp +++ b/crawler/SocketReader.cpp @@ -107,13 +107,18 @@ void SocketReader::httpsRequest(){ // Read from the SSL until there's no more data. - char buffer[ 10240 ]; + char * SSLBuffer = new char[ 11240 ]; int bytes; - - while ( ( bytes = SSL_read( ssl, buffer, - sizeof( buffer ) ) ) > 0 ) - write( 1, buffer, bytes ); - + while ( ( bytes = SSL_read( ssl, SSLBuffer, + 10240 ) ) > 0 ) + { + write( 1, SSLBuffer, bytes ); + size_t test = sizeof(SSLBuffer); + cout << test; + } + //write( 1, SSLBuffer, bytes ); + + buffer = SSLBuffer; SSL_shutdown( ssl ); SSL_free( ssl ); SSL_CTX_free( ctx ); diff --git a/crawler/spider.cpp b/crawler/spider.cpp index ebd6327..0569993 100644 --- a/crawler/spider.cpp +++ b/crawler/spider.cpp @@ -47,7 +47,9 @@ void Spider::FuncToRun() StreamReader *reader = request( currentUrl ); - + string pathToDisk = util::GetCurrentWorkingDir() + "/crawlerOutput/" + string(url.Host, strlen(url.Host)) + ".txt"; + int fd = util::writeToNewFileToLocation( reader->buffer, pathToDisk); + //parser.parse(reader); cond = true; } @@ -110,12 +112,12 @@ bool Spider::shouldURLbeCrawled( string url ) return false; } -/* + //check if path in url is in the robots txt bool Spider::checkRobots(string url_in) { ParsedUrl url = ParsedUrl(url_in); - string pathToRobots = util::GetCurrentWorkingDir() + "/robots/" + string(url.Host, strlen(url.Host)); + string pathToRobots = util::GetCurrentWorkingDir() + "/robots/" + string(url.Host, strlen(url.Host)) + ".txt"; int robotsFileD = util::getFileDescriptor(pathToRobots , "R"); //File does not exist yet if(robotsFileD == -1) @@ -123,7 +125,7 @@ bool Spider::checkRobots(string url_in) robotsFileD = getRobots(url); } - //char* robotsTXT = util::getFileMap(robotsFileD); + char* robotsTXT = util::getFileMap(robotsFileD); return 1; } @@ -134,23 +136,28 @@ int Spider::getRobots(ParsedUrl url ) { - string pathToDiskRobots = util::GetCurrentWorkingDir() + "/robots/" + string(url.Host, strlen(url.Host)); - string pathToWebRobots = "http://" + string(url.Host, strlen(url.Host)) + "/robots.txt"; + string pathToDiskRobots = util::GetCurrentWorkingDir() + "/robots/" + string(url.Host, strlen(url.Host)) + ".txt"; + string pathToWebRobots = "https://" + string(url.Host, strlen(url.Host)) + "/robots.txt"; //string(url.Service, strlen(url.Service))+ SocketReader *reader = new SocketReader(pathToWebRobots); reader->fillBuffer(); - int fd = util::writeToNewFileToLocation( reader->buffer, pathToDiskRobots); - if( fd == -1) + if(reader->buffer != NULL) { - cerr << "Error getting Robots.txt file " << endl; + int fd = util::writeToNewFileToLocation( reader->buffer, pathToDiskRobots); + if( fd == -1) + cerr << "Error getting Robots.txt file " << endl; + + return fd; } - return fd; - return 1; + cerr << "issue filling buffer from robots.txt" << endl; + return -1; + + }; -*/ + /* returns true if fileMap was created, otherwise false Modifies the filemap to be a char* of the file of the url passed diff --git a/docMap.txt b/docMap.txt index ece49798ba7e2549c46d70a86c453ae28e77b6f4..4806d35acdf8ff1ace8ffd6197e89ca69b84fe24 100644 GIT binary patch literal 3423 zcmeH}&rZWI48}R{Dc}J}v;l%~8;5Bp4qP}!X=4#<`BT-YLf@Wslp#TcHdeI^^-?Kq zoPPfKv)dII<IkUcY1GB!Y&EX_5s3g4i03j8)4`gF4xzZ`JjTeBL{r<%h2@@AYhID| z7C1TuuYYbcwW3`e&;o5FTfR+Z&<DREnPvH>`u2eLdrc7>MB-UR=z!?*dN#kAPp|Gi z%2rv-H3L=socbthYx0w!1&&2rV*v^!^W_U~R{YVIL5nBvq){YmK^mag6+RtvJEb5V zN(@L^EOM#9)?$gqiba&AqFs110KWCo@V-fB)jx@5s-(5-MYvaxM`|5DJn6*_WS!c% qM;NXHTI1Wl4&c37eR%S{+Wer&)&ZMjatDcMH+TEnm2Q^wcj^sf!@iRM delta 7 OcmcaF^@wxBBNhM;A_IE> diff --git a/robots/en.wikipedia.org.txt b/robots/en.wikipedia.org.txt new file mode 100755 index 0000000..293fb2b --- /dev/null +++ b/robots/en.wikipedia.org.txt @@ -0,0 +1,35 @@ +ki/Wikipedia%3AEditor_review +Disallow: /wiki/Wikipedia_talk:Editor_review +Disallow: /wiki/Wikipedia_talk%3AEditor_review +# +Disallow: /wiki/Wikipedia:Article_Incubator +Disallow: /wiki/Wikipedia%3AArticle_Incubator +Disallow: /wiki/Wikipedia_talk:Article_Incubator +Disallow: /wiki/Wikipedia_talk%3AArticle_Incubator +# +Disallow: /wiki/Category:Noindexed_pages +Disallow: /wiki/Category%3ANoindexed_pages +# +# </pre>lk:Arbitration_Committee_Elections +Disallow: /wiki/Wikipedia_talk%3AArbitration_Committee_Elections +# +Disallow: /wiki/Wikipedia:Mediation_Committee +Disallow: /wiki/Wikipedia%3AMediation_Committee +Disallow: /wiki/Wikipedia_talk:Mediation_Committee +Disallow: /wiki/Wikipedia_talk%3AMediation_Committee +# +Disallow: /wiki/Wikipedia:Mediation_Cabal/Cases +Disallow: /wiki/Wikipedia%3AMediation_Cabal/Cases +# +Disallow: /wiki/Wikipedia:Requests_for_bureaucratship +Disallow: /wiki/Wikipedia%3ARequests_for_bureaucratship +Disallow: /wiki/Wikipedia_talk:Requests_for_bureaucratship +Disallow: /wiki/Wikipedia_talk%3ARequests_for_bureaucratship +# +Disallow: /wiki/Wikipedia:Administrator_review +Disallow: /wiki/Wikipedia%3AAdministrator_review +Disallow: /wiki/Wikipedia_talk:Administrator_review +Disallow: /wiki/Wikipedia_talk%3AAdministrator_review +# +Disallow: /wiki/Wikipedia:Editor_review +Disallow: /wi \ No newline at end of file diff --git a/robots/www.bbc.com.txt b/robots/www.bbc.com.txt new file mode 100755 index 0000000..c79b6d0 --- /dev/null +++ b/robots/www.bbc.com.txt @@ -0,0 +1,40 @@ +HTTP/1.1 200 OK +Server: Apache +Last-Modified: Wed, 31 Jan 2018 00:26:36 GMT +ETag: "1f6-564078830c700" +Cache-Control: max-age=3600, public +Content-Type: text/plain +Content-Length: 502 +Accept-Ranges: bytes +Date: Thu, 22 Feb 2018 00:47:25 GMT +Via: 1.1 varnish +Age: 0 +Connection: close +X-Fastly-Cache-Status: MISS-CLUSTER +X-Served-By: cache-mdw17331-MDW +X-Cache: MISS +X-Cache-Hits: 0 +X-Timer: S1519260446.852690,VS0,VE114 +Vary: Accept-Encoding + +# v.4.6.6 +# HTTPS www.bbc.com +User-agent: * +Sitemap: https://www.bbc.com/sitemaps/https-index-com-archive.xml +Sitemap: https://www.bbc.com/sitemaps/https-index-com-news.xml + +Disallow: /cbbc/search/ +Disallow: /cbbc/search$ +Disallow: /cbbc/search? +Disallow: /cbeebies/search/ +Disallow: /cbeebies/search$ +Disallow: /cbeebies/search? +Disallow: /chwilio/ +Disallow: /chwilio$ +Disallow: /chwilio? +Disallow: /newsround +Disallow: /search/ +Disallow: /search$ +Disallow: /search? +Disallow: /food$ +Disallow: /food/ \ No newline at end of file diff --git a/robots/www.dailymail.co.uk.txt b/robots/www.dailymail.co.uk.txt new file mode 100755 index 0000000..8d32301 --- /dev/null +++ b/robots/www.dailymail.co.uk.txt @@ -0,0 +1,15 @@ +HTTP/1.1 200 OK +Content-Type: text/plain +Content-Length: 68 +Date: Thu, 22 Feb 2018 00:44:57 GMT +Connection: close +Vary: User-Agent + +# All Robots +User-agent: * + +# Disallow All Pages +Disallow: / + +ÝŽÿ®^øk ïž;r늇LêÚ˜Q û°—Òç¡¥–a&ª §Téª!ÕÒp´ÞÔAgpܬIšöÜPÐÖÆ‘1¦´\øøW¬tÔ#ÅØ[ÕÞ¨T›s&$az·jyÙ/· +-…OG†¨ÞŠx¹Á»M·á \ No newline at end of file diff --git a/robots/www.nytimes.com.txt b/robots/www.nytimes.com.txt new file mode 100755 index 0000000..a8298dc --- /dev/null +++ b/robots/www.nytimes.com.txt @@ -0,0 +1,16 @@ +HTTP/1.1 301 Moved Permanently +Server: Varnish +Retry-After: 0 +Content-Length: 0 +Location: http://www.nytimes.com/robots.txt +Accept-Ranges: bytes +Date: Thu, 22 Feb 2018 00:42:47 GMT +X-Frame-Options: DENY +Connection: close +X-API-Version: F-0 +X-PageType: legacy +Content-Security-Policy: default-src data: 'unsafe-inline' 'unsafe-eval' https:; script-src data: 'unsafe-inline' 'unsafe-eval' https: blob:; style-src data: 'unsafe-inline' https:; img-src data: https: blob:; font-src data: https:; connect-src https: wss:; media-src https: blob:; object-src https:; child-src https: data: blob:; form-action https:; block-all-mixed-content; +X-Served-By: cache-mdw17330-MDW +X-Cache: HIT +X-Cache-Hits: 0 + diff --git a/shared/urlTest.cpp b/shared/urlTest.cpp index a21e7f8..7fda4f8 100644 --- a/shared/urlTest.cpp +++ b/shared/urlTest.cpp @@ -12,21 +12,30 @@ using namespace std; int main(int argc, const char * argv[]) { - ParsedUrl test1 = ParsedUrl("https://developer.mozilla.org/en-US/docs/Learn" ) ; + ParsedUrl absoluteURLTest = ParsedUrl("https://developer.mozilla.org/en-US/docs/Learn" ) ; //string protocol = test1.getProtocol(); - test1.printUrl(); + absoluteURLTest.printUrl(); //assert( strcmp(test1.Service, "https") == 1); //assert( strcmp(test1.Host, "developer.mozilla.org") == 1); - ParsedUrl test2 = ParsedUrl("http://www.example.com/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument"); - test2.printUrl(); - assert( strcmp(test2.Service, "http")); - assert( strcmp(test2.Host, "example.com")); + ParsedUrl fragmentTest = ParsedUrl("http://www.example.com/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument"); + //fragmentTest.printUrl(); + //assert( strcmp(fragmentTest.Service, "http")); + //assert( strcmp(fragmentTest.Host, "example.com")); + ParsedUrl relativeURLTest = ParsedUrl("/wiki/List_of_sheep_breeds"); + relativeURLTest.printUrl(); + + + ParsedUrl pointToFragment = ParsedUrl("#topOfPage"); + + ParsedUrl mailToTest = ParsedUrl("mailto:someone@example.com?cc=someoneelse@example.com&bcc=andsomeoneelse@example.com\n" + "&subject=Summer%20Party&body=You%20are%20invited%20to%20a%20big%20summer%20party!\""); + mailToTest.printUrl(); std::cout << "URL TEST PASSED" << std::endl; } \ No newline at end of file diff --git a/tests/webSeed.txt b/tests/webSeed.txt index b4d953a..280e3f0 100644 --- a/tests/webSeed.txt +++ b/tests/webSeed.txt @@ -1,5 +1,7 @@ +https://en.wikipedia.org/wiki/71st_British_Academy_Film_Awards https://www.nytimes.com/ http://www.dailymail.co.uk/ushome/index.html http://www.bbc.com/ http://www.bbc.co.uk/news/business-42959138 -http://umich.edu \ No newline at end of file +http://umich.edu +https://en.wikipedia.org/wiki/North_Ronaldsay_sheep \ No newline at end of file diff --git a/url_test b/url_test index 1f663afde3cd05c74437f205d7fae5b2594a687e..f7ce01bbec97816e60e0c1312d56eb2ef36459b2 100755 GIT binary patch delta 4089 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 delta 3304 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 -- GitLab