From 1a1e887d3533a44404a4ab7f9937dc9dce0818ec Mon Sep 17 00:00:00 2001 From: westernmeadow Date: Fri, 25 Aug 2023 14:58:44 -0700 Subject: [PATCH] added new prepare_parser.py and cleaned output for marketplaces --- Forums/BestCardingWorld/__init__.py | 0 Forums/CryptBB/__init__.py | 0 Forums/CryptBB/captcha.png | Bin 16138 -> 0 bytes Forums/Initialization/forums_mining.py | 2 +- Forums/Initialization/prepare_parser.py | 18 +- .../AnonymousMarketplace/crawler_selenium.py | 20 +- MarketPlaces/AnonymousMarketplace/parser.py | 10 +- MarketPlaces/Apocalypse/crawler_selenium.py | 28 +- MarketPlaces/BlackPyramid/crawler_selenium.py | 25 +- MarketPlaces/CityMarket/crawler_selenium.py | 30 +- .../CypherMarketplace/crawler_selenium.py | 10 +- MarketPlaces/DarkFox/captcha.png | Bin 56639 -> 0 bytes MarketPlaces/DarkFox/crawler_selenium.py | 16 +- MarketPlaces/DarkMatter/crawler_selenium.py | 36 +- MarketPlaces/DarkTor/crawler_selenium.py | 18 +- .../DigitalThriftShop/crawler_selenium.py | 31 +- MarketPlaces/HiddenMarket/crawler_selenium.py | 50 +- MarketPlaces/Initialization/marketsList.txt | 9 +- MarketPlaces/Initialization/markets_mining.py | 33 +- MarketPlaces/Initialization/prepare_parser.py | 492 ++++++++++-------- .../LionMarketplace/crawler_selenium.py | 36 +- .../M00nkeyMarket/crawler_selenium.py | 32 +- .../MikesGrandStore/crawler_selenium.py | 60 +-- MarketPlaces/Nexus/crawler_selenium.py | 43 +- MarketPlaces/Nexus/parser.py | 6 +- .../RobinhoodMarket/crawler_selenium.py | 73 ++- MarketPlaces/ThiefWorld/crawler_selenium.py | 12 +- MarketPlaces/Tor2door/captcha.png | Bin 3370 -> 0 bytes MarketPlaces/Tor2door/crawler_selenium.py | 10 +- MarketPlaces/TorBay/crawler_selenium.py | 28 +- MarketPlaces/TorMarket/crawler_selenium.py | 38 +- MarketPlaces/ViceCity/crawler_selenium.py | 74 ++- setup.ini | 2 +- 33 files changed, 653 insertions(+), 589 deletions(-) delete mode 100644 Forums/BestCardingWorld/__init__.py delete mode 100644 Forums/CryptBB/__init__.py delete mode 100644 Forums/CryptBB/captcha.png delete mode 100644 MarketPlaces/DarkFox/captcha.png delete mode 100644 MarketPlaces/Tor2door/captcha.png diff --git a/Forums/BestCardingWorld/__init__.py b/Forums/BestCardingWorld/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Forums/CryptBB/__init__.py b/Forums/CryptBB/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Forums/CryptBB/captcha.png b/Forums/CryptBB/captcha.png deleted file mode 100644 index 08e45fc90dc94fa4769002bfd3e8107c35e4acfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16138 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%0DQ{Q4YFPn;Go0Dn#0eU-OsAc%RaN0 z-KHsa8mHKs^=OfVxVv(u7r-J&R56smk8*8ZdXb>9*G zdEXKKY2RV~aqnUN!`{Puyyp-f?>WSucOB+WJCE?u_TzlGavLA6oZ$D%xA70l$NBBj zF@8ONh+oei;#YGU{AzB4U(Bxa!OTH^KC{Np%B%dOw3i&oy^Fnl*7oyWV>o4-f@GQ>-W_f%f!xOG7kBz6f zvNg<8?&2+;^ptqgQ|1XznaABF9&?v?WGv4k<9Qw#EAa4Wo`;6>JUEc!{(&s_4`jJA zRN%p(JP!^Rcwo4|{X@lD+&5I>zTt9ZJ5c1ltwrwZFK};vfqMpuTplQJxj)a{{W&i7 zWx22=!-Xvw?%bT_{H7FVdy|~$O>wd}&9UALhk7%t_oi9vPO;XNWVJKFzK%G1+G6Z# ziL#?P%t}LuMQ4!t8b7nvNv16>O6GBL#!<3{5z@v{l7>+d`VnIKL84V6h6-W*z$aoo zl*sd$cGraJFvjXg zjNSb{4va?GvDwSsfe1S{2iex`WpyyY#%P9p{ZUrB{H%2OS+26w9b~CH$YOVp#qI#h zn3-4S4>GssL=h;m1uLT8YfwgB_ZLFO8K z%-4BYbXHh!dYN~6nR9xXt@ScfGsU!Hl9Izi$?lvDcIcP>>kpNN#ZqAL>yBD z9g_qclLT!Z{5B6hn+LDO&7{SJ$Lz*!axr0;U|c`OxM7S@{Rl(4K?bxw`qj;BRyWbB z_R=G7qD$(cOX{Ru>Y!cfpiQ*XD%w90>woY+`2U;#2L`2Qy%+<42><{907*qoM6N<$ Eg2uCN?f?J) diff --git a/MarketPlaces/DarkFox/crawler_selenium.py b/MarketPlaces/DarkFox/crawler_selenium.py index 0f7ee1d..8e1ca7b 100644 --- a/MarketPlaces/DarkFox/crawler_selenium.py +++ b/MarketPlaces/DarkFox/crawler_selenium.py @@ -30,7 +30,7 @@ baseURL = 'http://57d5j6bbwlpxbxe5tsjjy3vziktv3fo2o5j3nheo4gpg6lzpsimzqzid.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - opentor() + # opentor() mktName = getMKTName() driver = getAccess() @@ -42,7 +42,7 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - new_parse(mktName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -124,6 +124,7 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() return driver @@ -145,6 +146,7 @@ def getAccess(): # then allows for manual solving of captcha in the terminal #@param: current selenium web driver def captcha(driver): + ''' # wait for captcha page show up WebDriverWait(driver, 100).until(EC.visibility_of_element_located((By.XPATH, "/html/body/div/div/form/button[1]"))) @@ -168,6 +170,9 @@ def captcha(driver): # click the verify(submit) button driver.find_element(by=By.XPATH, value="/html/body/div/div/form/button[1]").click() + ''' + + input("Press ENTER when CAPTCHA is completed\n") # wait for listing page show up (This Xpath may need to change based on different seed url) WebDriverWait(driver, 100).until(EC.visibility_of_element_located( @@ -220,8 +225,7 @@ def getInterestedLinks(): # # Digital Products # links.append('http://57d5j6bbwlpxbxe5tsjjy3vziktv3fo2o5j3nheo4gpg6lzpsimzqzid.onion/category/0e384d5f-26ef-4561-b5a3-ff76a88ab781') # Software and Malware - # links.append('http://57d5j6bbwlpxbxe5tsjjy3vziktv3fo2o5j3nheo4gpg6lzpsimzqzid.onion/category/6b71210f-f1f9-4aa3-8f89-bd9ee28f7afc') - links.append('http://57d5j6bbwlpxbxe5tsjjy3vziktv3fo2o5j3nheo4gpg6lzpsimzqzid.onion/category/6b71210f-f1f9-4aa3-8f89-bd9ee28f7afc?page=15') + links.append('http://57d5j6bbwlpxbxe5tsjjy3vziktv3fo2o5j3nheo4gpg6lzpsimzqzid.onion/category/6b71210f-f1f9-4aa3-8f89-bd9ee28f7afc') # # Services # links.append('http://57d5j6bbwlpxbxe5tsjjy3vziktv3fo2o5j3nheo4gpg6lzpsimzqzid.onion/category/b9dc5846-5024-421e-92e6-09ba96a03280') # # Miscellaneous @@ -270,7 +274,7 @@ def crawlForum(driver): break # comment out - if count == 0: + if count == 1: break try: @@ -287,7 +291,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling BestCardingWorld forum done sucessfully. Press ENTER to continue\n") + print("Crawling the DarkFox market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/DarkMatter/crawler_selenium.py b/MarketPlaces/DarkMatter/crawler_selenium.py index c1eb457..e0babcb 100644 --- a/MarketPlaces/DarkMatter/crawler_selenium.py +++ b/MarketPlaces/DarkMatter/crawler_selenium.py @@ -32,7 +32,7 @@ baseURL = 'http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - opentor() + # opentor() mktName = getMKTName() driver = getAccess() @@ -44,7 +44,7 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - new_parse(mktName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -121,6 +121,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -185,15 +187,15 @@ def getNameFromURL(url): def getInterestedLinks(): links = [] - # digital fraud software - links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=76') - # legit - links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=78') + # # digital fraud software + # links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=76') + # # legit + # links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=78') # # hack guides - links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=94') + # links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=94') # # services - links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=117') - # # software/malware + # links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=117') + # software/malware links.append('http://darkmat3kdxestusl437urshpsravq7oqb7t3m36u2l62vnmmldzdmid.onion/market/products/?category=121') return links @@ -236,16 +238,14 @@ def crawlForum(driver): driver.back() # to keep from detecting click speed - # # comment out - # break - # - # # comment out - # if count == 1: - # break + # comment out + break + + # comment out + if count == 1: + break try: - # nav = driver.find_element(by=By.XPATH, value='/html/body/table[1]/tbody/tr/td/form/div/div[2]/table[2]') - # a = nav.find_element(by=By.LINK_TEXT, value=">") link = driver.find_element(by=By.LINK_TEXT, value=">").get_attribute('href') if link == "": raise NoSuchElementException @@ -258,7 +258,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling DarkMatter forum done sucessfully. Press ENTER to continue\n") + print("Crawling the DarkMatter market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/DarkTor/crawler_selenium.py b/MarketPlaces/DarkTor/crawler_selenium.py index 74e22be..24c2990 100644 --- a/MarketPlaces/DarkTor/crawler_selenium.py +++ b/MarketPlaces/DarkTor/crawler_selenium.py @@ -31,8 +31,8 @@ baseURL = 'http://zuauw53dukqdmll5p3fld26ns2gepcyfmbofobjczdni6ecmkoitnfid.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - opentor() - # mktName = getMKTName() + # opentor() + mktName = getMKTName() driver = getAccess() if driver != 'down': @@ -43,7 +43,7 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - # new_parse(forumName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -119,6 +119,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -186,10 +188,10 @@ def getInterestedLinks(): # Hacking links.append('http://zuauw53dukqdmll5p3fld26ns2gepcyfmbofobjczdni6ecmkoitnfid.onion/product-category/hacking-services/') - # Carding - links.append('http://zuauw53dukqdmll5p3fld26ns2gepcyfmbofobjczdni6ecmkoitnfid.onion/product-category/carding/') - # hacked paypals - links.append('http://zuauw53dukqdmll5p3fld26ns2gepcyfmbofobjczdni6ecmkoitnfid.onion/product-category/hacked-paypal-accounts/') + # # Carding + # links.append('http://zuauw53dukqdmll5p3fld26ns2gepcyfmbofobjczdni6ecmkoitnfid.onion/product-category/carding/') + # # hacked paypals + # links.append('http://zuauw53dukqdmll5p3fld26ns2gepcyfmbofobjczdni6ecmkoitnfid.onion/product-category/hacked-paypal-accounts/') return links @@ -248,7 +250,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling DarkTor forum done sucessfully. Press ENTER to continue\n") + print("Crawling the DarkTor market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/DigitalThriftShop/crawler_selenium.py b/MarketPlaces/DigitalThriftShop/crawler_selenium.py index 28424a8..132d2af 100644 --- a/MarketPlaces/DigitalThriftShop/crawler_selenium.py +++ b/MarketPlaces/DigitalThriftShop/crawler_selenium.py @@ -34,17 +34,17 @@ baseURL = 'http://kw4zlnfhxje7top26u57iosg55i7dzuljjcyswo2clgc3mdliviswwyd.onion def startCrawling(): # opentor() mktName = getMKTName() - # driver = getAccess() + driver = getAccess() - # if driver != 'down': - # try: - # login(driver) - # crawlForum(driver) - # except Exception as e: - # print(driver.current_url, e) - # closetor(driver) + if driver != 'down': + try: + login(driver) + crawlForum(driver) + except Exception as e: + print(driver.current_url, e) + closetor(driver) - new_parse(mktName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -91,7 +91,6 @@ def closetor(driver): def createFFDriver(): from MarketPlaces.Initialization.markets_mining import config - ff_binary = FirefoxBinary(config.get('TOR', 'firefox_binary_path')) ff_prof = FirefoxProfile(config.get('TOR', 'firefox_profile_path')) @@ -120,6 +119,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -189,8 +190,8 @@ def getInterestedLinks(): links.append('http://kw4zlnfhxje7top26u57iosg55i7dzuljjcyswo2clgc3mdliviswwyd.onion/product-category/botnets/') # # data leak # links.append('http://kw4zlnfhxje7top26u57iosg55i7dzuljjcyswo2clgc3mdliviswwyd.onion/product-category/dataleak/') - # databases - links.append('http://kw4zlnfhxje7top26u57iosg55i7dzuljjcyswo2clgc3mdliviswwyd.onion/product-category/databases/') + # # databases + # links.append('http://kw4zlnfhxje7top26u57iosg55i7dzuljjcyswo2clgc3mdliviswwyd.onion/product-category/databases/') # # ransomware # links.append('http://kw4zlnfhxje7top26u57iosg55i7dzuljjcyswo2clgc3mdliviswwyd.onion/product-category/ransomware/') # # rats @@ -234,10 +235,10 @@ def crawlForum(driver): driver.back() # comment out - # break + break # comment out - if count == 10: + if count == 1: break try: @@ -254,7 +255,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling DigitalThriftShop forum done sucessfully. Press ENTER to continue\n") + print("Crawling the DigitalThriftShop market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/HiddenMarket/crawler_selenium.py b/MarketPlaces/HiddenMarket/crawler_selenium.py index 1b3e1b5..3813c76 100644 --- a/MarketPlaces/HiddenMarket/crawler_selenium.py +++ b/MarketPlaces/HiddenMarket/crawler_selenium.py @@ -29,7 +29,7 @@ baseURL = 'http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion # Opens Tor Browser, crawls the website def startCrawling(): - opentor() + # opentor() marketName = getMKTName() driver = getAccess() @@ -41,7 +41,7 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - new_parse(marketName, baseURL, False) + new_parse(marketName, baseURL, True) # Opens Tor Browser @@ -161,6 +161,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -211,27 +213,27 @@ def getInterestedLinks(): links = [] # # Civil Software - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/civil_softwares') + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/civil_softwares') # # Tutorials - Carding - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/carding') - # # Digital - Hacks + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/carding') + # Digital - Hacks links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/hacks') - # Digital - Exploit Kit - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/exploit_kit') + # # Digital - Exploit Kit + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/exploit_kit') # # 0Day - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/0day') - # Digital Forensics - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/digital_forensics') - # Tutorials - Mining - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/mining') - # Tutorials - Worms - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/worms') - # Tutorials - Viruses - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/viruses') - # Tutorials - Trojans - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/trojans') - # Tutorials - Botnets - links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/botnets') + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/0day') + # # Digital Forensics + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/digital_forensics') + # # Tutorials - Mining + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/mining') + # # Tutorials - Worms + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/worms') + # # Tutorials - Viruses + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/viruses') + # # Tutorials - Trojans + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/trojans') + # # Tutorials - Botnets + # links.append('http://mipx6eedtsvfgfcmbm3utjedgjez2w4dzjdrbhtd2mt3cicwhhzspxqd.onion/category/botnets') return links @@ -275,11 +277,11 @@ def crawlForum(driver): driver.back() # comment out - # break + break # comment out - # if count == 2: - # break + if count == 1: + break try: pageCount += 1 @@ -296,7 +298,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling HiddenMarket market done sucessfully. Press ENTER to continue\n") + print("Crawling the HiddenMarket market done.") # Returns 'True' if the link is Topic link diff --git a/MarketPlaces/Initialization/marketsList.txt b/MarketPlaces/Initialization/marketsList.txt index 2e6ee5b..fe4ac4a 100644 --- a/MarketPlaces/Initialization/marketsList.txt +++ b/MarketPlaces/Initialization/marketsList.txt @@ -1 +1,8 @@ -HiddenMarket \ No newline at end of file +Apocalypse +DarkMatter +DigitalThriftShop +HiddenMarket +Nexus +Robinhood +TorBay +ViceCity \ No newline at end of file diff --git a/MarketPlaces/Initialization/markets_mining.py b/MarketPlaces/Initialization/markets_mining.py index ea8269a..4b9c02e 100644 --- a/MarketPlaces/Initialization/markets_mining.py +++ b/MarketPlaces/Initialization/markets_mining.py @@ -4,7 +4,6 @@ __author__ = 'DarkWeb' Starting point of the Darkweb Markets Mining ''' -import os from datetime import * from MarketPlaces.DarkFox.crawler_selenium import crawler as crawlerDarkFox from MarketPlaces.Tor2door.crawler_selenium import crawler as crawlerTor2door @@ -24,9 +23,11 @@ from MarketPlaces.ViceCity.crawler_selenium import crawler as crawlerViceCity from MarketPlaces.HiddenMarket.crawler_selenium import crawler as crawlerHiddenMarket from MarketPlaces.RobinhoodMarket.crawler_selenium import crawler as crawlerRobinhoodMarket from MarketPlaces.Nexus.crawler_selenium import crawler as crawlerNexus +from MarketPlaces.CypherMarketplace.crawler_selenium import crawler as crawlerCypher import configparser -import time +import os +import subprocess config = configparser.ConfigParser() config.read('../../setup.ini') @@ -71,18 +72,34 @@ def createDirectory(mkt): os.mkdir(descReadDir) +# Opens Tor Browser +def opentor(): + global pid + print("Connecting Tor...") + pro = subprocess.Popen(config.get('TOR', 'firefox_binary_path')) + pid = pro.pid + # time.sleep(7.5) + input('Press ENTER when Tor is connected to continue') + return + + if __name__ == '__main__': + + # opentor() + mktsList = getMarkets() for mkt in mktsList: mkt = mkt.replace('\n','') - print(f"Creating listing and description directories of {mkt} ...") + print("\nCreating listing and description directories ... for " + mkt) createDirectory(mkt) - time.sleep(5) - input("Directories created successfully. Press ENTER to continue\n") + print("Directories created.") if mkt == "DarkFox": + # for base in json["DarkFox"]["base"]: + # if crawlerDarkFox(base["url"], base["categories"]): + # break crawlerDarkFox() elif mkt == 'Tor2door': crawlerTor2door() @@ -118,7 +135,7 @@ if __name__ == '__main__': crawlerRobinhoodMarket() elif mkt == "Nexus": crawlerNexus() + elif mkt == "CypherMarketplace": + crawlerCypher() - - - print("Scraping process completed successfully!") + print("\nScraping process completed!") diff --git a/MarketPlaces/Initialization/prepare_parser.py b/MarketPlaces/Initialization/prepare_parser.py index afb85b2..c626b6a 100644 --- a/MarketPlaces/Initialization/prepare_parser.py +++ b/MarketPlaces/Initialization/prepare_parser.py @@ -20,9 +20,12 @@ from MarketPlaces.TorMarket.parser import * from MarketPlaces.HiddenMarket.parser import * from MarketPlaces.RobinhoodMarket.parser import * from MarketPlaces.Nexus.parser import * +from MarketPlaces.MikesGrandStore.parser import * from MarketPlaces.Classifier.classify_product import predict +nError = 0 + def mergePages(rmm, rec): @@ -82,13 +85,182 @@ def persist_data(url, row, cur): create_items(cur, row, marketPlace, vendor) +def incrementError(): + global nError + nError += 1 + + +def read_file(filePath, createLog, logFile): + try: + html = codecs.open(filePath.strip('\n'), encoding='utf8') + soup = BeautifulSoup(html, "html.parser") + html.close() + return soup + except: + + try: + html = open(filePath.strip('\n')) + soup = BeautifulSoup(html, "html.parser") + html.close() + return soup + except: + + incrementError() + print("There was a problem to read the file " + filePath) + if createLog: + logFile.write( + str(nError) + ". There was a problem to read the file " + filePath + "\n") + return None + + +def parse_listing(marketPlace, listingFile, soup, createLog, logFile): + try: + + if marketPlace == "DarkFox": + rw = darkfox_listing_parser(soup) + elif marketPlace == "Tor2door": + rw = tor2door_listing_parser(soup) + elif marketPlace == "Apocalypse": + rw = apocalypse_listing_parser(soup) + elif marketPlace == "ThiefWorld": + rw = thiefWorld_listing_parser(soup) + elif marketPlace == "AnonymousMarketplace": + rw = anonymousMarketplace_listing_parser(soup) + elif marketPlace == "ViceCity": + rw = vicecity_listing_parser(soup) + elif marketPlace == "TorBay": + rw = torbay_listing_parser(soup) + elif marketPlace == "M00nkeyMarket": + rw = m00nkey_listing_parser(soup) + elif marketPlace == "HiddenMarket": + rw = hiddenmarket_listing_parser(soup) + elif marketPlace == "DarkMatter": + rw = darkmatter_listing_parser(soup) + elif marketPlace == "DigitalThriftShop": + rw = digitalThriftShop_listing_parser(soup) + elif marketPlace == "LionMarketplace": + rw = lionmarketplace_listing_parser(soup) + elif marketPlace == "TorMarket": + rw = tormarket_listing_parser(soup) + elif marketPlace == "RobinhoodMarket": + rw = Robinhood_listing_parser(soup) + elif marketPlace == "Nexus": + rw = nexus_listing_parser(soup) + elif marketPlace == "MikesGrandStore": + rw = mikesGrandStore_listing_parser(soup) + else: + print("MISSING CALL TO LISTING PARSER IN PREPARE_PARSER.PY!") + raise Exception + return rw + + except: + + incrementError() + print("There was a problem to parse the file " + listingFile + " in the listing section!") + traceback.print_exc() + if createLog: + logFile.write( + str(nError) + ". There was a problem to parse the file " + listingFile + " in the Listing section.\n") + return None + + +def parse_description(marketPlace, descriptionFile, soup, createLog, logFile): + try: + + if marketPlace == "DarkFox": + rmm = darkfox_description_parser(soup) + elif marketPlace == "Tor2door": + rmm = tor2door_description_parser(soup) + elif marketPlace == "Apocalypse": + rmm = apocalypse_description_parser(soup) + elif marketPlace == "ThiefWorld": + rmm = thiefWorld_description_parser(soup) + elif marketPlace == "AnonymousMarketplace": + rmm = anonymousMarketplace_description_parser(soup) + elif marketPlace == "ViceCity": + rmm = vicecity_description_parser(soup) + elif marketPlace == "TorBay": + rmm = torbay_description_parser(soup) + elif marketPlace == "M00nkeyMarket": + rmm = m00nkey_description_parser(soup) + elif marketPlace == "HiddenMarket": + rmm = hiddenmarket_description_parser(soup) + elif marketPlace == "DarkMatter": + rmm = darkmatter_description_parser(soup) + elif marketPlace == "DigitalThriftShop": + rmm = digitalThriftShop_description_parser(soup) + elif marketPlace == "LionMarketplace": + rmm = lionmarketplace_description_parser(soup) + elif marketPlace == "TorMarket": + rmm = tormarket_description_parser(soup) + elif marketPlace == "RobinhoodMarket": + rmm = Robinhood_description_parser(soup) + elif marketPlace == "Nexus": + rmm = nexus_description_parser(soup) + elif marketPlace == "MikesGrandStore": + rmm = mikesGrandStore_description_parser(soup) + else: + print("MISSING CALL TO DESCRIPTION PARSER IN PREPARE_PARSER.PY!") + raise Exception + return rmm + + except: + + incrementError() + print("There was a problem to parse the file " + descriptionFile + " in the Description section!") + traceback.print_exc() + if createLog: + logFile.write( + str(nError) + ". There was a problem to parse the file " + descriptionFile + " in the Description section.\n") + return None + + +def persist_record(url, rec, cur, con, createLog, logFile, listingFile, descriptionFile): + try: + persist_data(url, tuple(rec), cur) + con.commit() + return True + except: + + con.rollback() + + trace = traceback.format_exc() + + if trace.find("already exists") == -1: + incrementError() + print(f"There was a problem to persist the files ({listingFile} + {descriptionFile}) in the database!") + traceback.print_exc() + if createLog: + logFile.write( + str(nError) + f". There was a problem to persist the files ({listingFile} + {descriptionFile}) in the database!\n") + return False + else: + return True + + +def move_file(filePath, createLog, logFile): + # source = line2.replace(os.path.basename(line2), "") + filename + source = filePath + destination = filePath.replace(os.path.basename(filePath), "") + r'Read/' + + try: + shutil.move(source, destination) + return True + except: + + print("There was a problem to move the file " + filePath) + incrementError() + if createLog: + logFile.write( + str(nError) + ". There was a problem to move the file " + filePath + "\n") + return False + + def new_parse(marketPlace, url, createLog): from MarketPlaces.Initialization.markets_mining import config, CURRENT_DATE - print("Parsing the " + marketPlace + " marketplace and conduct data classification to store the information in the database.") - - # ini = time.time() + print("Parsing the " + marketPlace + " market and conduct data classification to store the information in the database.") # Connecting to the database con = connectDataBase() @@ -97,271 +269,131 @@ def new_parse(marketPlace, url, createLog): # Creating the tables (The database should be created manually) create_database(cur, con) - nError = 0 - - lines = [] # listing pages - lns = [] # description pages - detPage = {} + mainDir = os.path.join(config.get('Project', 'shared_folder'), "MarketPlaces/" + marketPlace + "/HTML_Pages") - #Creating the log file for each Market Place + # Creating the log file for each Forum if createLog: - if not os.path.exists("./" + marketPlace + "/Logs/" + marketPlace + "_" + CURRENT_DATE + ".log"): - logFile = open("./" + marketPlace + "/Logs/" + marketPlace + "_" + CURRENT_DATE + ".log", "w") - else: - print("Files of the date " + CURRENT_DATE + " from the Market Place " + marketPlace + - " were already read. Delete the referent information in the Data Base and also delete the log file" - " in the _Logs folder to read files from this Market Place of this date again.") - raise SystemExit - - mainDir = os.path.join(config.get('Project', 'shared_folder'), "MarketPlaces/" + marketPlace + "/HTML_Pages") + try: + logFile = open(mainDir + f"/{CURRENT_DATE}/" + marketPlace + "_" + CURRENT_DATE + ".log", "w") + except: + print("Could not open log file!") + createLog = False + logFile = None + # raise SystemExit + else: + logFile = None # Reading the Listing Html Pages - for fileListing in glob.glob(os.path.join(mainDir, CURRENT_DATE + "\\Listing", '*.html')): - lines.append(fileListing) + listings = glob.glob(os.path.join(mainDir, CURRENT_DATE + "\\Listing", '*.html')) + for listingIndex, listingFile in enumerate(listings): - # Reading the Description Html Pages - for fileDescription in glob.glob(os.path.join(mainDir, CURRENT_DATE + "\\Description", '*.html')): - lns.append(fileDescription) + print("Reading listing folder of '" + marketPlace + "', file '" + os.path.basename(listingFile) + "', index= " + str( + listingIndex + 1) + " ... " + str(len(listings))) - # Parsing the Description Pages and put the tag's content into a dictionary (Hash table) - for index, line2 in enumerate(lns): + listingSoup = read_file(listingFile, createLog, logFile) - print("Reading description folder of '" + marketPlace + "', file '" + os.path.basename(line2) + "', index= " + str(index + 1) + " ... " + str(len(lns))) + # listing flags + doParseListing = listingSoup is not None + doDescription = False - try: - html = codecs.open(line2.strip('\n'), encoding='utf8') - soup = BeautifulSoup(html, "html.parser") - html.close() - except: + readDescriptionError = False + parseDescriptionError = False + persistDescriptionError = False + moveDescriptionError = False + findDescriptionError = False - try: - html = open(line2.strip('\n')) - soup = BeautifulSoup(html, "html.parser") - html.close() - except: + rw = [] - nError += 1 - print("There was a problem to read the file " + line2 + " in the Description section!") - if createLog: - logFile.write(str(nError) + ". There was a problem to read the file " + line2 + " in the Description section.\n") - continue + if doParseListing: - try: + rw = parse_listing(marketPlace, listingFile, listingSoup, createLog, logFile) - if marketPlace == "DarkFox": - rmm = darkfox_description_parser(soup) - elif marketPlace == "Tor2door": - rmm = tor2door_description_parser(soup) - elif marketPlace == "Apocalypse": - rmm = apocalypse_description_parser(soup) - elif marketPlace == "ThiefWorld": - rmm = thiefWorld_description_parser(soup) - elif marketPlace =="AnonymousMarketplace": - rmm = anonymousMarketplace_description_parser(soup) - elif marketPlace == "ViceCity": - rmm = vicecity_description_parser(soup) - elif marketPlace == "TorBay": - rmm = torbay_description_parser(soup) - elif marketPlace == "M00nkeyMarket": - rmm = m00nkey_description_parser(soup) - elif marketPlace == "HiddenMarket": - rmm = hiddenmarket_description_parser(soup) - elif marketPlace == "DarkMatter": - rmm = darkmatter_description_parser(soup) - elif marketPlace == "DigitalThriftShop": - rmm = digitalThriftShop_description_parser(soup) - elif marketPlace == "LionMarketplace": - rmm = lionmarketplace_description_parser(soup) - elif marketPlace == "TorMarket": - rmm = tormarket_description_parser(soup) - elif marketPlace == "RobinhoodMarket": - rmm = Robinhood_description_parser(soup) - elif marketPlace == "Nexus": - rmm = nexus_description_parser(soup) - # key = u"Pr:" + rmm[0].upper()[:desc_lim1] + u" Vendor:" + rmm[13].upper()[:desc_lim2] - key = u"Url:" + os.path.basename(line2).replace(".html", "") - - # save file address with description record in memory - detPage[key] = {'rmm': rmm, 'filename': os.path.basename(line2)} - - except Exception as e: - raise e - - nError += 1 - print("There was a problem to parse the file " + line2 + " in the Description section!") - if createLog: - logFile.write(str(nError) + ". There was a problem to parse the file " + line2 + " in the Description section.\n") + doDescription = rw is not None - # Parsing the Listing Pages and put the tag's content into a list - for index, line1 in enumerate(lines): + if doDescription: - print("Reading listing folder of '" + marketPlace + "', file '" + os.path.basename(line1) + "', index= " + str(index + 1) + " ... " + str(len(lines))) + nFound = 0 - readError = False - try: - html = codecs.open(line1.strip('\n'), encoding='utf8') - soup = BeautifulSoup(html, "html.parser") - html.close() - except: + for rec in rw: - try: - html = open(line1.strip('\n')) - soup = BeautifulSoup(html, "html.parser") - html.close() - except Exception as e: - raise e - nError += 1 - print("There was a problem to read the file " + line1 + " in the Listing section!") - if createLog: - logFile.write(str(nError) + ". There was a problem to read the file " + line1 + " in the Listing section.\n") - readError = True - - if not readError: - - parseError = False - try: - - if marketPlace == "DarkFox": - rw = darkfox_listing_parser(soup) - elif marketPlace == "Tor2door": - rw = tor2door_listing_parser(soup) - elif marketPlace == "Apocalypse": - rw = apocalypse_listing_parser(soup) - elif marketPlace == "ThiefWorld": - rw = thiefWorld_listing_parser(soup) - elif marketPlace == "AnonymousMarketplace": - rw = anonymousMarketplace_listing_parser(soup) - elif marketPlace == "ViceCity": - rw = vicecity_listing_parser(soup) - elif marketPlace == "TorBay": - rw = torbay_listing_parser(soup) - elif marketPlace == "M00nkeyMarket": - rw = m00nkey_listing_parser(soup) - elif marketPlace == "HiddenMarket": - rw =hiddenmarket_listing_parser(soup) - elif marketPlace == "DarkMatter": - rw = darkmatter_listing_parser(soup) - elif marketPlace == "DigitalThriftShop": - rw = digitalThriftShop_listing_parser(soup) - elif marketPlace == "LionMarketplace": - rw = lionmarketplace_listing_parser(soup) - elif marketPlace == "TorMarket": - rw = tormarket_listing_parser(soup) - elif marketPlace == "RobinhoodMarket": - rw = Robinhood_listing_parser(soup) - elif marketPlace == "Nexus": - rw = nexus_listing_parser(soup) - else: - parseError = True - - except Exception as e: - - nError += 1 - print("There was a problem to parse the file " + line1 + " in the listing section!") - if createLog: - logFile.write( - str(nError) + ". There was a problem to parse the file " + line1 + " in the Listing section.\n") - parseError = True + rec = rec.split(',') - if not parseError: + descriptionPattern = cleanLink(rec[20]) + ".html" - persistError = False - moveError = False - num_in_db = 0 - num_persisted_moved = 0 + # Reading the associated description Html Pages + descriptions = glob.glob(os.path.join(mainDir, CURRENT_DATE + "\\Description", descriptionPattern)) - for rec in rw: + nFound += len(descriptions) - rec = rec.split(',') + for descriptionIndex, descriptionFile in enumerate(descriptions): - # if len(detPage) > 0: #It was created here just because Zeroday Market does not have Description Pages - # key = rec[23] + print("Reading description folder of '" + marketPlace + "', file '" + os.path.basename( + descriptionFile) + "', index= " + str(descriptionIndex + 1) + " ... " + str(len(descriptions))) - # key = u"Pr:" + rec[1].upper()[:list_lim1] + u" Vendor:" + rec[18].upper()[:list_lim2] - key = u"Url:" + cleanLink(rec[20]) + descriptionSoup = read_file(descriptionFile, createLog, logFile) - # if the associated description page is parsed - if key in detPage: + # description flags + doParseDescription = descriptionSoup is not None + doPersistRecord = False + doMoveDescription = False - # rec = mergePages(detPage, rec) + rmm = [] + + if doParseDescription: + + rmm = parse_description(marketPlace, descriptionFile, descriptionSoup, createLog, logFile) + + doPersistRecord = rmm is not None + + else: + readDescriptionError = True + parseDescriptionError = True + + if doPersistRecord: # Combining the information from Listing and Description Pages - rmm = detPage[key]['rmm'] rec = mergePages(rmm, rec) - # Append to the list the classification of the product - # rec.append(str(predict(rec[1], rec[5], language='markets'))) + # Append to the list the classification of the topic rec.append(str(predict(rec[4], rec[5], language='sup_english'))) # Persisting the information in the database - try: - persist_data(url, tuple(rec), cur) - con.commit() - except Exception as e: - - trace = traceback.format_exc() - - if trace.find("already exists") == -1: - nError += 1 - print("There was a problem to persist the file " + detPage[key]['filename'] + " in the database!") - if createLog: - logFile.write( - str(nError) + ". There was a problem to persist the file " + detPage[key]['filename'] + " in the database.\n") - persistError = True - - con.rollback() - - if not persistError: - - # move description files of completed folder - source = line2.replace(os.path.basename(line2), "") + detPage[key]['filename'] - destination = line2.replace(os.path.basename(line2), "") + r'Read/' - - try: - shutil.move(source, destination) - num_persisted_moved += 1 - except: - - print("There was a problem to move the file " + detPage[key]['filename'] + " in the Description section!") - nError += 1 - if createLog: - logFile.write( - str(nError) + ". There was a problem to move the file " + detPage[key]['filename'] + " in the Description section!.\n") - moveError = True - - # if the associated description page is not read or not parsed + persistSuccess = persist_record(url, rec, cur, con, createLog, logFile, listingFile, + descriptionFile) + + doMoveDescription = persistSuccess + else: - # query database - # if the product already exists: - # num_in_db += 1 - pass + parseDescriptionError = True - # if number of products on listing page is equal to - # the number of merged, persisted, and moved products plus - # the number of products already in the database - if not persistError and not moveError and len(rw) == (num_persisted_moved + num_in_db): + if doMoveDescription: - # move listing file to completed folder - source = line1 - destination = line1.replace(os.path.basename(line1), "") + r'Read/' + # move description files of completed folder + moveSuccess = move_file(descriptionFile, createLog, logFile) - try: - shutil.move(source, destination) - except: + if not moveSuccess: + moveDescriptionError = True - nError += 1 - print("There was a problem to move the file " + line1 + " in the Listing section!") - if createLog: - logFile.write(str(nError) + ". There was a problem to move the file " + line1 + " in the Listing section!.\n") + else: + moveDescriptionError = True - # g.close () + if not (nFound > 0): - if createLog: - logFile.close() + findDescriptionError = True + + incrementError() + print(f"There was a problem to locate the file(s) for {listingFile} in the Description section!") + if createLog: + logFile.write( + str(nError) + f". There was a problem to locate the file(s) for {listingFile}" + f" in the Description section!\n") - # end = time.time() + if not (readDescriptionError or parseDescriptionError or persistDescriptionError + or moveDescriptionError or findDescriptionError): + # move listing files of completed folder + move_file(listingFile, createLog, logFile) - # finalTime = float(end-ini) + if createLog: + logFile.close() - # print (marketPlace + " Parsing Perfomed Succesfully in %.2f" %finalTime + "!") - input("Parsing the " + marketPlace + " marketplace and data classification done successfully. Press ENTER to continue\n") + print("Parsing the " + marketPlace + " market and data classification done.") diff --git a/MarketPlaces/LionMarketplace/crawler_selenium.py b/MarketPlaces/LionMarketplace/crawler_selenium.py index 7558452..237838f 100644 --- a/MarketPlaces/LionMarketplace/crawler_selenium.py +++ b/MarketPlaces/LionMarketplace/crawler_selenium.py @@ -31,19 +31,19 @@ baseURL = 'http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - opentor() + # opentor() mktName = getMKTName() driver = getAccess() if driver != 'down': try: - login(driver) + # login(driver) crawlForum(driver) except Exception as e: print(driver.current_url, e) closetor(driver) - new_parse(mktName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -103,7 +103,7 @@ def createFFDriver(): ff_prof.set_preference("network.cookie.lifetimePolicy", 2) ff_prof.set_preference("network.dns.disablePrefetch", True) ff_prof.set_preference("network.http.sendRefererHeader", 0) - ff_prof.set_preference("permissions.default.image", 2) + ff_prof.set_preference("permissions.default.image", 1) ff_prof.set_preference("browser.download.folderList", 2) ff_prof.set_preference("browser.download.manager.showWhenStarting", False) ff_prof.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/plain") @@ -119,6 +119,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -187,12 +189,12 @@ def getInterestedLinks(): # Software/Malware links.append('http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion/category/16') - # Carding - links.append('http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion/category/20') - # Hacking - links.append('http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion/category/ba142ac0-c7e7-11ec-9bd1-fdd89c3d3f91') - # tutorial - links.append('http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion/category/19') + # # Carding + # links.append('http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion/category/20') + # # Hacking + # links.append('http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion/category/ba142ac0-c7e7-11ec-9bd1-fdd89c3d3f91') + # # tutorial + # links.append('http://lionznqc2hg2wsp5vgruqait4cpknihwlje6hkjyi52lcl5ivyf7bcad.onion/category/19') return links @@ -231,12 +233,12 @@ def crawlForum(driver): savePage(driver, driver.page_source, item) driver.back() - # # comment out - # break - # - # # comment out - # if count == 1: - # break + # comment out + break + + # comment out + if count == 1: + break try: link = driver.find_element(by=By.XPATH, value= @@ -252,7 +254,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling LionMarketplace forum done sucessfully. Press ENTER to continue\n") + print("Crawling the LionMarketplace market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/M00nkeyMarket/crawler_selenium.py b/MarketPlaces/M00nkeyMarket/crawler_selenium.py index 83413fc..ccd8f11 100644 --- a/MarketPlaces/M00nkeyMarket/crawler_selenium.py +++ b/MarketPlaces/M00nkeyMarket/crawler_selenium.py @@ -34,17 +34,17 @@ MARKET_NAME = 'M00nkeyMarket' #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): # opentor() - # driver = getAccess() - # - # if driver != 'down': - # try: - # login(driver) - # crawlForum(driver) - # except Exception as e: - # print(driver.current_url, e) - # closetor(driver) + driver = getAccess() - new_parse(MARKET_NAME, BASE_URL, False) + if driver != 'down': + try: + login(driver) + crawlForum(driver) + except Exception as e: + print(driver.current_url, e) + closetor(driver) + + new_parse(MARKET_NAME, BASE_URL, True) # Opens Tor Browser @@ -120,6 +120,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -203,7 +205,7 @@ def getInterestedLinks(): # software links.append('http://moonkey4f2mkcp6hpackeea356puiry27h3dz3hzbt3adbmsk4gs7wyd.onion/search/subcategories?subcategory=30') # # guides - links.append('http://moonkey4f2mkcp6hpackeea356puiry27h3dz3hzbt3adbmsk4gs7wyd.onion/search/subcategories?subcategory=17') + # links.append('http://moonkey4f2mkcp6hpackeea356puiry27h3dz3hzbt3adbmsk4gs7wyd.onion/search/subcategories?subcategory=17') return links @@ -243,11 +245,11 @@ def crawlForum(driver): driver.back() # comment out - # break + break # comment out - # if count == 1: - # break + if count == 1: + break try: link = driver.find_element(by=By.LINK_TEXT, value='Next ›').get_attribute('href') @@ -262,7 +264,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling M00nkeyMarket done sucessfully. Press ENTER to continue\n") + print("Crawling the M00nkeyMarket done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/MikesGrandStore/crawler_selenium.py b/MarketPlaces/MikesGrandStore/crawler_selenium.py index b6b67ac..cd45464 100644 --- a/MarketPlaces/MikesGrandStore/crawler_selenium.py +++ b/MarketPlaces/MikesGrandStore/crawler_selenium.py @@ -31,47 +31,19 @@ baseURL = 'http://4yx2akutmkhwfgzlpdxiah7cknurw6vlddlq24fxa3r3ebophwgpvhyd.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - print("Welcome to the darkweb pipeline. Do you want to run:") - print("[A] Entire pipeline\t[B] Crawler only\t[C] Parser only") - choice = input() - - while choice not in {'A', 'B', 'C'}: - print("Choose the options below only!") - print("[A] Entire pipeline\t[B] Crawler only\t[C] Parser only") - choice = input() - - if choice == 'A': - opentor() - mktName = getMKTName() - driver = getAccess() - - if driver != 'down': - try: - login(driver) - crawlForum(driver) - except Exception as e: - print(driver.current_url, e) - closetor(driver) - - new_parse(mktName, baseURL, False) - - - if choice == 'B': - opentor() - driver = getAccess() - - if driver != 'down': - try: - login(driver) - crawlForum(driver) - except Exception as e: - print(driver.current_url, e) - closetor(driver) - - - if choice == 'C': - mktName = getMKTName() - new_parse(mktName, baseURL, False) + # opentor() + mktName = getMKTName() + driver = getAccess() + + if driver != 'down': + try: + login(driver) + crawlForum(driver) + except Exception as e: + print(driver.current_url, e) + closetor(driver) + + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -131,7 +103,7 @@ def createFFDriver(): ff_prof.set_preference("network.cookie.lifetimePolicy", 2) ff_prof.set_preference("network.dns.disablePrefetch", True) ff_prof.set_preference("network.http.sendRefererHeader", 0) - ff_prof.set_preference("permissions.default.image", 2) + ff_prof.set_preference("permissions.default.image", 1) ff_prof.set_preference("browser.download.folderList", 2) ff_prof.set_preference("browser.download.manager.showWhenStarting", False) ff_prof.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/plain") @@ -147,6 +119,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -275,7 +249,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling MikesGrandStore forum done sucessfully. Press ENTER to continue\n") + print("Crawling the MikesGrandStore market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/Nexus/crawler_selenium.py b/MarketPlaces/Nexus/crawler_selenium.py index 4ae2a21..70e1480 100644 --- a/MarketPlaces/Nexus/crawler_selenium.py +++ b/MarketPlaces/Nexus/crawler_selenium.py @@ -31,7 +31,7 @@ baseURL = 'http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - opentor() + # opentor() mktName = getMKTName() driver = getAccess() @@ -40,9 +40,9 @@ def startCrawling(): crawlForum(driver) except Exception as e: print(driver.current_url, e) - closetor(driver) + closetor(driver) - new_parse(mktName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser #prompts for ENTER input to continue @@ -116,6 +116,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -131,8 +133,8 @@ def getAccess(): driver.close() return 'down' -def savePage(page, url): - cleanPage = cleanHTML(page) +def savePage(driver, page, url): + cleanPage = cleanHTML(driver, page) filePath = getFullPathName(url) os.makedirs(os.path.dirname(filePath), exist_ok=True) open(filePath, 'wb').write(cleanPage.encode('utf-8')) @@ -173,14 +175,14 @@ def getInterestedLinks(): # Bot nets links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/malware/botnets/') - # Rats - links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/malware/rats/') - # Ransomware - links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/malware/ransomware/') - # Other Malware - links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/malware/outros-malware/') - # Hacking Tools & Scripting - links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/hacking-spam/ferramentas-de-hacking-scripts/') + # # Rats + # links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/malware/rats/') + # # Ransomware + # links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/malware/ransomware/') + # # Other Malware + # links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/malware/outros-malware/') + # # Hacking Tools & Scripting + # links.append('http://nexus2bmba34euohk3xo7og2zelkgbtc2p7rjsbxrjjknlecja2tdvyd.onion/categoria-produto/hacking-spam/ferramentas-de-hacking-scripts/') return links @@ -207,7 +209,7 @@ def crawlForum(driver): except: driver.refresh() html = driver.page_source - savePage(html, link) + savePage(driver, html, link) list = productPages(html) for item in list: @@ -216,9 +218,16 @@ def crawlForum(driver): driver.get(itemURL) except: driver.refresh() - savePage(driver.page_source, item) + savePage(driver, driver.page_source, item) driver.back() + # comment out + break + + # comment out + if count == 1: + break + try: link = driver.find_element(by=By.XPATH, value= '/html/body/div[1]/div[2]/div/div/main/nav/ul/li[3]/a').get_attribute('href') @@ -233,7 +242,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling Nexus done sucessfully. Press ENTER to continue\n") + print("Crawling the Nexus market done.") # Returns 'True' if the link is a description link @@ -263,5 +272,5 @@ def productPages(html): def crawler(): startCrawling() - print("Crawling and Parsing Nexus .... DONE!") + # print("Crawling and Parsing Nexus .... DONE!") diff --git a/MarketPlaces/Nexus/parser.py b/MarketPlaces/Nexus/parser.py index 1b28984..3c0cfb6 100644 --- a/MarketPlaces/Nexus/parser.py +++ b/MarketPlaces/Nexus/parser.py @@ -107,7 +107,7 @@ def nexus_listing_parser(soup): # Finding the name of the product name_of_product = product.find("h2", {"class": "woocommerce-loop-product__title"}).find("a").text name_of_product_cleaned = cleanString(name_of_product.strip()) - print(name_of_product_cleaned) + # print(name_of_product_cleaned) name.append(name_of_product_cleaned) #finding the URL try: @@ -135,8 +135,8 @@ def nexus_listing_parser(soup): qLeft.append("-1") shipFrom.append("-1") shipTo.append("-1") - print("Done! moving onto the next product!") - print(len(shipTo)) + # print("Done! moving onto the next product!") + # print(len(shipTo)) nm += 1 except AttributeError as e: print("I'm somewhere I don't belong. I'm going to leave") diff --git a/MarketPlaces/RobinhoodMarket/crawler_selenium.py b/MarketPlaces/RobinhoodMarket/crawler_selenium.py index ab22f78..9124a8f 100644 --- a/MarketPlaces/RobinhoodMarket/crawler_selenium.py +++ b/MarketPlaces/RobinhoodMarket/crawler_selenium.py @@ -1,7 +1,7 @@ __author__ = 'chris' ''' -WeTheNorth Market Crawler (Selenium) +RobinhoodMarket Market Crawler (Selenium) ''' from selenium import webdriver @@ -23,8 +23,6 @@ from MarketPlaces.Initialization.prepare_parser import new_parse from MarketPlaces.RobinhoodMarket.parser import Robinhood_links_parser from MarketPlaces.Utilities.utilities import cleanHTML -config = configparser.ConfigParser() -config.read('../../setup.ini') counter = 1 baseURL = 'http://ilr3qzubfnx33vbhal7l5coo4ftqlkv2tboph4ujog5crz6m5ua2b2ad.onion/' @@ -34,15 +32,14 @@ def startCrawling(): # Opening tor beforehand gives "Tor exited during startup error" # opentor() - marketName = getMarketName() + marketName = getMKTName() driver = getAccess() - # Captcha - input("Press ENTER when website has loaded") - if driver != 'down': try: + # Captcha + input("Press ENTER when website has loaded") # Robinhood doesn't need login # login(driver) crawlForum(driver) @@ -50,11 +47,13 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - new_parse(marketName, baseURL, False) + new_parse(marketName, baseURL, True) # Opens Tor Browser def opentor(): + from MarketPlaces.Initialization.markets_mining import config + global pid print("Connecting Tor...") pro = subprocess.Popen(config.get('TOR', 'firefox_binary_path')) @@ -70,7 +69,7 @@ def login(driver): # Returns the name of the website -def getMarketName(): +def getMKTName(): name = 'RobinhoodMarket' return name @@ -96,6 +95,8 @@ def closetor(driver): # Creates FireFox 'driver' and configure its 'Profile' # to use Tor proxy and socket def createFFDriver(): + from MarketPlaces.Initialization.markets_mining import config + ff_binary = FirefoxBinary(config.get('TOR', 'firefox_binary_path')) ff_prof = FirefoxProfile(config.get('TOR', 'firefox_profile_path')) @@ -124,13 +125,14 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver def getAccess(): url = getFixedURL() driver = createFFDriver() - input('Tor Connected. Press ENTER to continue\n') try: driver.get(url) return driver @@ -150,12 +152,14 @@ def savePage(driver, page, url): # Gets the full path of the page to be saved along with its appropriate file name def getFullPathName(url): - from MarketPlaces.Initialization.markets_mining import CURRENT_DATE + from MarketPlaces.Initialization.markets_mining import config, CURRENT_DATE + + mainDir = os.path.join(config.get('Project', 'shared_folder'), "MarketPlaces/" + getMKTName() + "/HTML_Pages") fileName = getNameFromURL(url) if isDescriptionLink(url): - fullPath = r'..\RobinhoodMarket\HTML_Pages\\' + CURRENT_DATE + r'\\Description\\' + fileName + '.html' + fullPath = os.path.join(mainDir, CURRENT_DATE + r'\\Description\\' + fileName + '.html') else: - fullPath = r'..\RobinhoodMarket\HTML_Pages\\' + CURRENT_DATE + r'\\Listing\\' + fileName + '.html' + fullPath = os.path.join(mainDir, CURRENT_DATE + r'\\Listing\\' + fileName + '.html') return fullPath @@ -174,8 +178,8 @@ def getInterestedLinks(): # Hacking links.append('http://ilr3qzubfnx33vbhal7l5coo4ftqlkv2tboph4ujog5crz6m5ua2b2ad.onion/product-category/hacking/') - # Other Software - links.append('http://ilr3qzubfnx33vbhal7l5coo4ftqlkv2tboph4ujog5crz6m5ua2b2ad.onion/product-category/other-software/') + # # Other Software + # links.append('http://ilr3qzubfnx33vbhal7l5coo4ftqlkv2tboph4ujog5crz6m5ua2b2ad.onion/product-category/other-software/') return links @@ -184,25 +188,24 @@ def crawlForum(driver): print("Crawling the Robinhood market") linksToCrawl = getInterestedLinks() - visited = set(linksToCrawl) - initialTime = time.time() i = 0 - count = 0 while i < len(linksToCrawl): link = linksToCrawl[i] print('Crawling :', link) try: - try: - driver.get(link) - except: - driver.refresh() - html = driver.page_source - savePage(driver, html, link) - has_next_page = True + count = 0 + while has_next_page: + try: + driver.get(link) + except: + driver.refresh() + html = driver.page_source + savePage(driver, html, link) + list = productPages(html) for item in list: @@ -213,27 +216,20 @@ def crawlForum(driver): driver.refresh() savePage(driver, driver.page_source, item) driver.back() + # comment out - # break + break # comment out - # if count == 1: - # count = 0 - # break + if count == 1: + break # go to next page of market try: nav = driver.find_element(by=By.XPATH, value="//a[@class='next page-numbers']") link = nav.get_attribute('href') - if link == "": raise NoSuchElementException - try: - driver.get(link) - except: - driver.refresh() - html = driver.page_source - savePage(driver, html, link) count += 1 except NoSuchElementException: @@ -243,10 +239,7 @@ def crawlForum(driver): print(link, e) i += 1 - # finalTime = time.time() - # print finalTime - initialTime - - input("Crawling Robinhood market done successfully. Press ENTER to continue\n") + print("Crawling the Robinhood market done.") # Returns 'True' if the link is Topic link diff --git a/MarketPlaces/ThiefWorld/crawler_selenium.py b/MarketPlaces/ThiefWorld/crawler_selenium.py index 194f449..345bdbe 100644 --- a/MarketPlaces/ThiefWorld/crawler_selenium.py +++ b/MarketPlaces/ThiefWorld/crawler_selenium.py @@ -1,7 +1,7 @@ __author__ = 'Helium' ''' -ThiefWorld Forum Crawler (Selenium) +ThiefWorld Market Crawler (Selenium) ''' from selenium import webdriver @@ -32,7 +32,7 @@ baseURL = 'http://qsw7iurcrdwyml5kg4oxbmtqrcnpxiag3iumdarefzeunnyc2dnyljad.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - opentor() + # opentor() mktName = getMKTName() driver = getAccess() @@ -44,7 +44,7 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - # new_parse(mktName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -104,7 +104,7 @@ def createFFDriver(): ff_prof.set_preference("network.cookie.lifetimePolicy", 2) ff_prof.set_preference("network.dns.disablePrefetch", True) ff_prof.set_preference("network.http.sendRefererHeader", 0) - ff_prof.set_preference("permissions.default.image", 2) + ff_prof.set_preference("permissions.default.image", 1) ff_prof.set_preference("browser.download.folderList", 2) ff_prof.set_preference("browser.download.manager.showWhenStarting", False) ff_prof.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/plain") @@ -120,6 +120,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -260,7 +262,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling ThiefWorld forum done sucessfully. Press ENTER to continue\n") + print("Crawling the ThiefWorld market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/Tor2door/captcha.png b/MarketPlaces/Tor2door/captcha.png deleted file mode 100644 index 39bd6f2efacb93dc21eb32f1a3f7f7f365481de4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3370 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 A0RR91 diff --git a/MarketPlaces/Tor2door/crawler_selenium.py b/MarketPlaces/Tor2door/crawler_selenium.py index 858ddcf..ec2e37f 100644 --- a/MarketPlaces/Tor2door/crawler_selenium.py +++ b/MarketPlaces/Tor2door/crawler_selenium.py @@ -29,8 +29,8 @@ baseURL = 'http://yzrrne3pveltulbavydr2kiashvlnysdwclwmklo6cyjuqpxi7ku4xqd.onion # Opens Tor Browser, crawls the website def startCrawling(): - opentor() - # marketName = getMKTName() + # opentor() + marketName = getMKTName() driver = getAccess() if driver != 'down': @@ -41,7 +41,7 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - # new_parse(marketName, baseURL, False) + new_parse(marketName, baseURL, True) # Opens Tor Browser @@ -161,6 +161,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -278,7 +280,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling Tor2door market done sucessfully. Press ENTER to continue\n") + print("Crawling the Tor2door market done.") # Returns 'True' if the link is Topic link diff --git a/MarketPlaces/TorBay/crawler_selenium.py b/MarketPlaces/TorBay/crawler_selenium.py index 7968089..5035999 100644 --- a/MarketPlaces/TorBay/crawler_selenium.py +++ b/MarketPlaces/TorBay/crawler_selenium.py @@ -34,17 +34,17 @@ baseURL = 'http://torbay3253zck4ym5cbowwvrbfjjzruzthrx3np5y6owvifrnhy5ybid.onion def startCrawling(): # opentor() mktName = getMKTName() - # driver = getAccess() - # - # if driver != 'down': - # try: - # login(driver) - # crawlForum(driver) - # except Exception as e: - # print(driver.current_url, e) - # closetor(driver) - # - new_parse(mktName, baseURL, False) + driver = getAccess() + + if driver != 'down': + try: + login(driver) + crawlForum(driver) + except Exception as e: + print(driver.current_url, e) + closetor(driver) + + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -120,6 +120,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver #the driver 'gets' the url, attempting to get on the site, if it can't access return 'down' @@ -230,7 +232,7 @@ def crawlForum(driver): # comment out if count == 1: - break + break try: link = driver.find_element(by=By.XPATH, value= @@ -246,7 +248,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling TorBay forum done sucessfully. Press ENTER to continue\n") + print("Crawling the TorBay market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/TorMarket/crawler_selenium.py b/MarketPlaces/TorMarket/crawler_selenium.py index 7569045..b76fb1c 100644 --- a/MarketPlaces/TorMarket/crawler_selenium.py +++ b/MarketPlaces/TorMarket/crawler_selenium.py @@ -33,17 +33,17 @@ baseURL = 'http://22222253ebafysmwyrl4uxfcs2xm6k7zb4xyse2csne73atvxu53gfad.onion def startCrawling(): # opentor() mktName = getMKTName() - # driver = getAccess() - # - # if driver != 'down': - # try: - # login(driver) - # crawlForum(driver) - # except Exception as e: - # print(driver.current_url, e) - # closetor(driver) + driver = getAccess() - new_parse(mktName, baseURL, False) + if driver != 'down': + try: + # login(driver) + crawlForum(driver) + except Exception as e: + print(driver.current_url, e) + closetor(driver) + + new_parse(mktName, baseURL, True) # Opens Tor Browser @@ -103,7 +103,7 @@ def createFFDriver(): ff_prof.set_preference("network.cookie.lifetimePolicy", 2) ff_prof.set_preference("network.dns.disablePrefetch", True) ff_prof.set_preference("network.http.sendRefererHeader", 0) - ff_prof.set_preference("permissions.default.image", 2) + ff_prof.set_preference("permissions.default.image", 1) ff_prof.set_preference("browser.download.folderList", 2) ff_prof.set_preference("browser.download.manager.showWhenStarting", False) ff_prof.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/plain") @@ -119,6 +119,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver @@ -184,12 +186,12 @@ def getNameFromURL(url): def getInterestedLinks(): links = [] - # Hacking Tutorials - links.append('http://22222253ebafysmwyrl4uxfcs2xm6k7zb4xyse2csne73atvxu53gfad.onion/product-category/guides-tutorials/hacking/') - # # Malware + # # Hacking Tutorials + # links.append('http://22222253ebafysmwyrl4uxfcs2xm6k7zb4xyse2csne73atvxu53gfad.onion/product-category/guides-tutorials/hacking/') + # Malware links.append('http://22222253ebafysmwyrl4uxfcs2xm6k7zb4xyse2csne73atvxu53gfad.onion/product-category/malware/') # # Hacking Services - links.append('http://22222253ebafysmwyrl4uxfcs2xm6k7zb4xyse2csne73atvxu53gfad.onion/product-category/services/hacking-services/') + # links.append('http://22222253ebafysmwyrl4uxfcs2xm6k7zb4xyse2csne73atvxu53gfad.onion/product-category/services/hacking-services/') return links @@ -232,8 +234,8 @@ def crawlForum(driver): break # comment out - # if count == 1: - # break + if count == 1: + break try: link = driver.find_element(by=By.XPATH, value= @@ -249,7 +251,7 @@ def crawlForum(driver): print(link, e) i += 1 - input("Crawling TorMarket forum done sucessfully. Press ENTER to continue\n") + print("Crawling the TorMarket market done.") # Returns 'True' if the link is a description link diff --git a/MarketPlaces/ViceCity/crawler_selenium.py b/MarketPlaces/ViceCity/crawler_selenium.py index 91b08cd..cf7ea82 100644 --- a/MarketPlaces/ViceCity/crawler_selenium.py +++ b/MarketPlaces/ViceCity/crawler_selenium.py @@ -25,8 +25,6 @@ from MarketPlaces.Initialization.prepare_parser import new_parse from MarketPlaces.ViceCity.parser import vicecity_links_parser from MarketPlaces.Utilities.utilities import cleanHTML -config = configparser.ConfigParser() -config.read('../../setup.ini') counter = 1 baseURL = 'http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/' @@ -34,7 +32,7 @@ baseURL = 'http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion # Opens Tor Browser, crawls the website, then parses, then closes tor #acts like the main method for the crawler, another function at the end of this code calls this function later def startCrawling(): - opentor() + # opentor() mktName = getMKTName() driver = getAccess() @@ -46,12 +44,14 @@ def startCrawling(): print(driver.current_url, e) closetor(driver) - new_parse(mktName, baseURL, False) + new_parse(mktName, baseURL, True) # Opens Tor Browser #prompts for ENTER input to continue def opentor(): + from MarketPlaces.Initialization.markets_mining import config + global pid print("Connecting Tor...") pro = subprocess.Popen(config.get('TOR', 'firefox_binary_path')) @@ -90,6 +90,8 @@ def closetor(driver): # Creates FireFox 'driver' and configure its 'Profile' # to use Tor proxy and socket def createFFDriver(): + from MarketPlaces.Initialization.markets_mining import config + ff_binary = FirefoxBinary(config.get('TOR', 'firefox_binary_path')) ff_prof = FirefoxProfile(config.get('TOR', 'firefox_profile_path')) @@ -118,6 +120,8 @@ def createFFDriver(): driver = webdriver.Firefox(firefox_binary=ff_binary, firefox_profile=ff_prof, service=service) + driver.maximize_window() + return driver #the driver 'gets' the url, attempting to get on the site, if it can't access return 'down' @@ -140,9 +144,9 @@ def login(driver): # wait for first captcha page to show up (This Xpath may need to change based on different seed url) WebDriverWait(driver, 100).until(EC.visibility_of_element_located( (By.XPATH, "/html/body/div/div/form/div/div[1]"))) - input("Press Enter once captcha done (dont press done)") + input("Press Enter once captcha done") #clicks button after captcha is inputted - driver.find_element(by=By.XPATH, value='/html/body/div/div/form/button').click() + # driver.find_element(by=By.XPATH, value='/html/body/div/div/form/button').click() #wait for login page to show up WebDriverWait(driver, 100).until(EC.visibility_of_element_located( @@ -152,9 +156,9 @@ def login(driver): userBox.send_keys('ct1234') #waits for second catpcha to be inputted by user - input("Press Enter once captcha done (dont press continue)") + input("Press Enter once captcha done") #clicks on continue - driver.find_element(by=By.XPATH, value='/html/body/div/div/div/form/input[2]').click() + # driver.find_element(by=By.XPATH, value='/html/body/div/div/div/form/input[2]').click() #waits for password to show WebDriverWait(driver, 100).until(EC.visibility_of_element_located( @@ -220,12 +224,12 @@ def getInterestedLinks(): # Digital - Fraud Software, Has Hacking and Guides links.append('http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/?category=150') - # Digital - Guides and Tutorials - links.append('http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/?category=94') - # Carding Services - links.append('http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/?category=155') - # Digital - Other (half junk half random stuff like: bots, rats, viruses, and guides) - links.append('http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/?category=153') + # # Digital - Guides and Tutorials + # links.append('http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/?category=94') + # # Carding Services + # links.append('http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/?category=155') + # # Digital - Other (half junk half random stuff like: bots, rats, viruses, and guides) + # links.append('http://52qlucglu6fuaqist2herssakipapig2higaaayu7446n55xw4ylxqid.onion/?category=153') return links @@ -237,26 +241,24 @@ def crawlForum(driver): print("Crawling the ViceCity Market") linksToCrawl = getInterestedLinks() - visited = set(linksToCrawl) - initialTime = time.time() - count = 0 i = 0 while i < len(linksToCrawl): link = linksToCrawl[i] print('Crawling :', link) try: - try: - driver.get(link) - except: - driver.refresh() - html = driver.page_source - savePage(driver, html, link) - has_next_page = True + count = 0 + while has_next_page: + try: + driver.get(link) + except: + driver.refresh() + html = driver.page_source + savePage(driver, html, link) + list = productPages(html) - j = 0 for item in list: itemURL = urlparse.urljoin(baseURL, str(item)) try: @@ -268,25 +270,18 @@ def crawlForum(driver): time.sleep(2.5) # so site doesnt crash driver.back() - #comment out - # break + # comment out + break - # # comment out - # if count == 1: - # count = 0 - # break + # comment out + if count == 1: + break try: temp = driver.find_element(by=By.CLASS_NAME, value='pagination') link = temp.find_element(by=By.LINK_TEXT, value='Next').get_attribute('href') if link == "": raise NoSuchElementException - try: - driver.get(link) - except: - driver.refresh() - html = driver.page_source - savePage(driver, html, link) count += 1 except NoSuchElementException: @@ -296,10 +291,7 @@ def crawlForum(driver): print(link, e) i += 1 - # finalTime = time.time() - # print finalTime - initialTime - - input("Crawling ViceCity done sucessfully. Press ENTER to continue\n") + print("Crawling the ViceCity market done.") # Returns 'True' if the link is a description link diff --git a/setup.ini b/setup.ini index 41b32d0..f4c18df 100644 --- a/setup.ini +++ b/setup.ini @@ -15,4 +15,4 @@ password = password database = darkweb_markets_forums [Encryption] -secret = "password" \ No newline at end of file +secret = password \ No newline at end of file