ieomyidae - eomyidae - a gopher crawler software Err bitreich.org 70 hgit clone git://bitreich.org/eomyidae URL:git://bitreich.org/eomyidae bitreich.org 70 1Log /scm/eomyidae/log.gph bitreich.org 70 1Files /scm/eomyidae/files.gph bitreich.org 70 1Refs /scm/eomyidae/refs.gph bitreich.org 70 1Tags /scm/eomyidae/tag bitreich.org 70 1README /scm/eomyidae/file/README.md.gph bitreich.org 70 1LICENSE /scm/eomyidae/file/LICENSE.gph bitreich.org 70 i--- Err bitreich.org 70 ieomyidae (15243B) Err bitreich.org 70 i--- Err bitreich.org 70 i 1 #!/usr/bin/env python Err bitreich.org 70 i 2 # coding=utf-8 Err bitreich.org 70 i 3 # Err bitreich.org 70 i 4 # See the LICENSE file for details. Err bitreich.org 70 i 5 # Err bitreich.org 70 i 6 Err bitreich.org 70 i 7 import os Err bitreich.org 70 i 8 import sys Err bitreich.org 70 i 9 import getopt Err bitreich.org 70 i 10 import urllib.parse Err bitreich.org 70 i 11 import socket Err bitreich.org 70 i 12 import io Err bitreich.org 70 i 13 import pickle Err bitreich.org 70 i 14 import time Err bitreich.org 70 i 15 import hashlib Err bitreich.org 70 i 16 import errno Err bitreich.org 70 i 17 import random Err bitreich.org 70 i 18 import operator Err bitreich.org 70 i 19 import math Err bitreich.org 70 i 20 from multiprocessing import Pool Err bitreich.org 70 i 21 from datetime import datetime Err bitreich.org 70 i 22 from datetime import timedelta Err bitreich.org 70 i 23 Err bitreich.org 70 i 24 def parseuri(uri): Err bitreich.org 70 i 25 urls = urllib.parse.urlparse(uri, allow_fragments=False) Err bitreich.org 70 i 26 if ":" in urls.netloc: Err bitreich.org 70 i 27 (host, port) = urls.netloc.split(":")[:2] Err bitreich.org 70 i 28 else: Err bitreich.org 70 i 29 host = urls.netloc Err bitreich.org 70 i 30 port = 70 Err bitreich.org 70 i 31 Err bitreich.org 70 i 32 mtype = "1" Err bitreich.org 70 i 33 if len(urls.path) > 1: Err bitreich.org 70 i 34 mtype = urls.path[1] Err bitreich.org 70 i 35 Err bitreich.org 70 i 36 if len(urls.path) > 2: Err bitreich.org 70 i 37 if len(urls.query) > 0: Err bitreich.org 70 i 38 selector = "%s?%s" % (urls.path[2:], urls.query) Err bitreich.org 70 i 39 else: Err bitreich.org 70 i 40 selector = urls.path[2:] Err bitreich.org 70 i 41 else: Err bitreich.org 70 i 42 selector = "" Err bitreich.org 70 i 43 Err bitreich.org 70 i 44 return (host, port, mtype, selector) Err bitreich.org 70 i 45 Err bitreich.org 70 i 46 def poolgopher(req): Err bitreich.org 70 i 47 data = gopher(req[0], req[1], req[2], req[3]) Err bitreich.org 70 i 48 req.append(data) Err bitreich.org 70 i 49 return req Err bitreich.org 70 i 50 Err bitreich.org 70 i 51 def gopher(uri=None, host=None, port=70, selector=""): Err bitreich.org 70 i 52 #print("gopher(uri = %s, host = %s, port = %d, selector = %s)" % \ Err bitreich.org 70 i 53 # (uri, host, port, selector)) Err bitreich.org 70 i 54 if uri != None: Err bitreich.org 70 i 55 (host, port, mtype, selector) = parseuri(uri) Err bitreich.org 70 i 56 port = int(port) Err bitreich.org 70 i 57 Err bitreich.org 70 i 58 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) Err bitreich.org 70 i 59 s.settimeout(20) Err bitreich.org 70 i 60 try: Err bitreich.org 70 i 61 s.connect((host, port)) Err bitreich.org 70 i 62 except socket.gaierror: Err bitreich.org 70 i 63 return "" Err bitreich.org 70 i 64 except socket.timeout: Err bitreich.org 70 i 65 return "" Err bitreich.org 70 i 66 except TimeoutError: Err bitreich.org 70 i 67 return "" Err bitreich.org 70 i 68 except ConnectionResetError: Err bitreich.org 70 i 69 return "" Err bitreich.org 70 i 70 except OverflowError: Err bitreich.org 70 i 71 return "" Err bitreich.org 70 i 72 except OSError as e: Err bitreich.org 70 i 73 # No route to host. Err bitreich.org 70 i 74 if e.errno == 113: Err bitreich.org 70 i 75 return "" Err bitreich.org 70 i 76 Err bitreich.org 70 i 77 try: Err bitreich.org 70 i 78 s.send(("%s\r\n" % (selector)).encode("utf-8")) Err bitreich.org 70 i 79 except BrokenPipeError: Err bitreich.org 70 i 80 return "" Err bitreich.org 70 i 81 Err bitreich.org 70 i 82 fd = s.makefile("b") Err bitreich.org 70 i 83 try: Err bitreich.org 70 i 84 data = fd.read() Err bitreich.org 70 i 85 except socket.timeout: Err bitreich.org 70 i 86 fd.close() Err bitreich.org 70 i 87 return "" Err bitreich.org 70 i 88 except ConnectionResetError: Err bitreich.org 70 i 89 fd.close() Err bitreich.org 70 i 90 return "" Err bitreich.org 70 i 91 fd.close() Err bitreich.org 70 i 92 Err bitreich.org 70 i 93 try: Err bitreich.org 70 i 94 content = data.decode(errors='replace') Err bitreich.org 70 i 95 except UnicodeDecodeError: Err bitreich.org 70 i 96 content = data.decode("iso-8859-1") Err bitreich.org 70 i 97 Err bitreich.org 70 i 98 return content Err bitreich.org 70 i 99 Err bitreich.org 70 i 100 def parsemenu(data): Err bitreich.org 70 i 101 menu = [] Err bitreich.org 70 i 102 lines = data.split("\n") Err bitreich.org 70 i 103 for line in lines: Err bitreich.org 70 i 104 line = line.strip() Err bitreich.org 70 i 105 if len(line) < 1: Err bitreich.org 70 i 106 continue Err bitreich.org 70 i 107 Err bitreich.org 70 i 108 mtype = line[0] Err bitreich.org 70 i 109 Err bitreich.org 70 i 110 # Last entry Err bitreich.org 70 i 111 if mtype == ".": Err bitreich.org 70 i 112 break Err bitreich.org 70 i 113 Err bitreich.org 70 i 114 elements = line[1:].split("\t") Err bitreich.org 70 i 115 if len(elements) < 4: Err bitreich.org 70 i 116 continue Err bitreich.org 70 i 117 (description, selector, host, port) = elements[:4] Err bitreich.org 70 i 118 menu.append([mtype, description, selector, host, port]) Err bitreich.org 70 i 119 Err bitreich.org 70 i 120 return menu Err bitreich.org 70 i 121 Err bitreich.org 70 i 122 def menu2text(menu): Err bitreich.org 70 i 123 text = "" Err bitreich.org 70 i 124 for entry in menu: Err bitreich.org 70 i 125 if type(entry[1]) != str: Err bitreich.org 70 i 126 continue Err bitreich.org 70 i 127 Err bitreich.org 70 i 128 text += "%s\n" % (entry[1]) Err bitreich.org 70 i 129 Err bitreich.org 70 i 130 return text Err bitreich.org 70 i 131 Err bitreich.org 70 i 132 ## Robots.txt Err bitreich.org 70 i 133 # https://en.wikipedia.org/wiki/Robots.txt Err bitreich.org 70 i 134 # # Comment Err bitreich.org 70 i 135 # User-agent: somebot Err bitreich.org 70 i 136 # Disallow: /path Err bitreich.org 70 i 137 # Allow: /path Err bitreich.org 70 i 138 # Crawl-delay: seconds Err bitreich.org 70 i 139 def parserobots(data): Err bitreich.org 70 i 140 robots = [] Err bitreich.org 70 i 141 lines = data.split("\n") Err bitreich.org 70 i 142 for line in lines: Err bitreich.org 70 i 143 line = line.strip() Err bitreich.org 70 i 144 if "#" in line: Err bitreich.org 70 i 145 (line, comment) = line.split("#", 1) Err bitreich.org 70 i 146 if len(line) < 0: Err bitreich.org 70 i 147 # Empty line, needed for bot-specific rules. Err bitreich.org 70 i 148 robots.append(["",""]) Err bitreich.org 70 i 149 continue Err bitreich.org 70 i 150 if not ":" in line: Err bitreich.org 70 i 151 continue Err bitreich.org 70 i 152 Err bitreich.org 70 i 153 (header, value) = line.strip().split(":", 1) Err bitreich.org 70 i 154 value = value.strip().lower() Err bitreich.org 70 i 155 header = header.strip().lower() Err bitreich.org 70 i 156 robots.append([header, value]) Err bitreich.org 70 i 157 return robots Err bitreich.org 70 i 158 Err bitreich.org 70 i 159 def adaptrobots(robotsdata): Err bitreich.org 70 i 160 filterlines = {} Err bitreich.org 70 i 161 robotslines = parserobots(robotsdata) Err bitreich.org 70 i 162 i = 0 Err bitreich.org 70 i 163 Err bitreich.org 70 i 164 allowlines = [] Err bitreich.org 70 i 165 disallowlines = [] Err bitreich.org 70 i 166 otherlines = [] Err bitreich.org 70 i 167 iseomyidae = False Err bitreich.org 70 i 168 while i < len(robotslines): Err bitreich.org 70 i 169 header = robotslines[i][0].lower() Err bitreich.org 70 i 170 value = robotslines[i][1] Err bitreich.org 70 i 171 if header == "user-agent": Err bitreich.org 70 i 172 ua = value.split("/") Err bitreich.org 70 i 173 if ua[0] == "eomyidae" or ua[0] == "*": Err bitreich.org 70 i 174 iseomyidae = 1 Err bitreich.org 70 i 175 else: Err bitreich.org 70 i 176 iseomyidae = 0 Err bitreich.org 70 i 177 elif header == "allow" and iseomyidae == True: Err bitreich.org 70 i 178 allowlines.append(value) Err bitreich.org 70 i 179 elif header == "disallow" and iseomyidae == True: Err bitreich.org 70 i 180 disallowlines.append(value) Err bitreich.org 70 i 181 elif header == "": Err bitreich.org 70 i 182 iseomyidae = False Err bitreich.org 70 i 183 else: Err bitreich.org 70 i 184 if iseomyidae == True: Err bitreich.org 70 i 185 otherlines.append([header, value]) Err bitreich.org 70 i 186 i += 1 Err bitreich.org 70 i 187 Err bitreich.org 70 i 188 filterlines["allow"] = allowlines Err bitreich.org 70 i 189 filterlines["disallow"] = disallowlines Err bitreich.org 70 i 190 filterlines["other"] = otherlines Err bitreich.org 70 i 191 if len(allowlines) > 0 or len(disallowlines) > 0 \ Err bitreich.org 70 i 192 or len(otherlines) > 0: Err bitreich.org 70 i 193 filterlines["empty"] = False Err bitreich.org 70 i 194 else: Err bitreich.org 70 i 195 filterlines["empty"] = True Err bitreich.org 70 i 196 Err bitreich.org 70 i 197 return filterlines Err bitreich.org 70 i 198 Err bitreich.org 70 i 199 def mkpath(cachepath): Err bitreich.org 70 i 200 try: Err bitreich.org 70 i 201 os.makedirs(cachepath) Err bitreich.org 70 i 202 except OSError as e: Err bitreich.org 70 i 203 if e.errno != errno.EEXIST: Err bitreich.org 70 i 204 raise Err bitreich.org 70 i 205 Err bitreich.org 70 i 206 def mkopen(cachefile): Err bitreich.org 70 i 207 if not os.path.exists(cachefile): Err bitreich.org 70 i 208 fd = open(cachefile, "xb") Err bitreich.org 70 i 209 else: Err bitreich.org 70 i 210 fd = open(cachefile, "wb") Err bitreich.org 70 i 211 return fd Err bitreich.org 70 i 212 Err bitreich.org 70 i 213 def informserveradmin(uri, host=None, port=70): Err bitreich.org 70 i 214 if host == None: Err bitreich.org 70 i 215 (host, port, mtype, selector) = parseuri(uri) Err bitreich.org 70 i 216 port = int(port) Err bitreich.org 70 i 217 Err bitreich.org 70 i 218 # We are nice and inform before every robots.txt, how to contact us. Err bitreich.org 70 i 219 gopher(host=host, port=port, selector="This is eomyidae, your " Err bitreich.org 70 i 220 "friendly crawler. See " Err bitreich.org 70 i 221 "gopher://gopherproject.org/1/eomyidae for " Err bitreich.org 70 i 222 "more info. Have a nice day!") Err bitreich.org 70 i 223 Err bitreich.org 70 i 224 def cacherobots(cachedir, uri, host=None, port=70, force=False, \ Err bitreich.org 70 i 225 filtercache=None): Err bitreich.org 70 i 226 if host == None: Err bitreich.org 70 i 227 (host, port, mtype, selector) = parseuri(uri) Err bitreich.org 70 i 228 port = int(port) Err bitreich.org 70 i 229 Err bitreich.org 70 i 230 if filtercache != None and host in filtercache: Err bitreich.org 70 i 231 #print("Got filterlines from memory filtercache.") Err bitreich.org 70 i 232 return filtercache[host] Err bitreich.org 70 i 233 Err bitreich.org 70 i 234 print("Getting robots for %s:%d" % (host, port)) Err bitreich.org 70 i 235 Err bitreich.org 70 i 236 cachepath = "%s/%s:%d" % (cachedir, host, port) Err bitreich.org 70 i 237 mkpath(cachepath) Err bitreich.org 70 i 238 Err bitreich.org 70 i 239 cacherobotstxt = "%s/robots.txt" % (cachepath) Err bitreich.org 70 i 240 cacherobotspickle = "%s/robots.pickle" % (cachepath) Err bitreich.org 70 i 241 filterlines = {} Err bitreich.org 70 i 242 if not os.path.exists(cacherobotstxt) or force == True: Err bitreich.org 70 i 243 # Be nice. Err bitreich.org 70 i 244 informserveradmin(uri=uri, host=host, port=port) Err bitreich.org 70 i 245 Err bitreich.org 70 i 246 robotsdata = gopher(host=host, port=port, selector="/robots.txt") Err bitreich.org 70 i 247 print("Got new robots.txt.") Err bitreich.org 70 i 248 print(robotsdata) Err bitreich.org 70 i 249 robotstxtfd = mkopen(cacherobotstxt) Err bitreich.org 70 i 250 robotstxtfd.write(robotsdata.encode()) Err bitreich.org 70 i 251 robotstxtfd.close() Err bitreich.org 70 i 252 Err bitreich.org 70 i 253 filterlines = adaptrobots(robotsdata) Err bitreich.org 70 i 254 # Do not store if there is nothing, so we save I/O later. Err bitreich.org 70 i 255 if filterlines["empty"] == False: Err bitreich.org 70 i 256 print("Storing filterlines.") Err bitreich.org 70 i 257 storelistdb(cacherobotspickle, filterlines) Err bitreich.org 70 i 258 Err bitreich.org 70 i 259 else: Err bitreich.org 70 i 260 if os.path.exists(cacherobotspickle): Err bitreich.org 70 i 261 #print("Loading filterlines from cache.") Err bitreich.org 70 i 262 filterlines = loadlistdb(cacherobotspickle) Err bitreich.org 70 i 263 else: Err bitreich.org 70 i 264 #print("No filterlines available in cache.") Err bitreich.org 70 i 265 filterlines["empty"] = True Err bitreich.org 70 i 266 Err bitreich.org 70 i 267 #print(filterlines) Err bitreich.org 70 i 268 if filtercache != None: Err bitreich.org 70 i 269 filtercache[host] = filterlines Err bitreich.org 70 i 270 Err bitreich.org 70 i 271 return filterlines Err bitreich.org 70 i 272 Err bitreich.org 70 i 273 def selectorisallowed(filterlines, selector): Err bitreich.org 70 i 274 if filterlines["empty"] == True: Err bitreich.org 70 i 275 return True Err bitreich.org 70 i 276 Err bitreich.org 70 i 277 def robotsmatch(pattern, selector): Err bitreich.org 70 i 278 #print("pattern = %s, selector = %s" % (pattern, selector)) Err bitreich.org 70 i 279 if pattern == '*': Err bitreich.org 70 i 280 #print("Just start match.") Err bitreich.org 70 i 281 return True Err bitreich.org 70 i 282 elif pattern[0] == '*': Err bitreich.org 70 i 283 #print("Begins with star.") Err bitreich.org 70 i 284 if pattern[-1] == '*': Err bitreich.org 70 i 285 #print("Begins and ends with star.") Err bitreich.org 70 i 286 if pattern[1:-1] in selector: Err bitreich.org 70 i 287 #print("Matches.") Err bitreich.org 70 i 288 return True Err bitreich.org 70 i 289 else: Err bitreich.org 70 i 290 return False Err bitreich.org 70 i 291 else: Err bitreich.org 70 i 292 return selector.endswith(pattern[1:]) Err bitreich.org 70 i 293 elif pattern[-1] == '*': Err bitreich.org 70 i 294 #print("Ends with star.") Err bitreich.org 70 i 295 return selector.startswith(pattern[:-1]) Err bitreich.org 70 i 296 else: Err bitreich.org 70 i 297 return selector.startswith(pattern) Err bitreich.org 70 i 298 Err bitreich.org 70 i 299 isallowed = True Err bitreich.org 70 i 300 for line in filterlines["disallow"]: Err bitreich.org 70 i 301 # TODO: Should this be match everything? Err bitreich.org 70 i 302 if len(line) == 0: Err bitreich.org 70 i 303 continue Err bitreich.org 70 i 304 if robotsmatch(line, selector) == True: Err bitreich.org 70 i 305 #print("isallowed = False") Err bitreich.org 70 i 306 isallowed = False Err bitreich.org 70 i 307 for line in filterlines["allow"]: Err bitreich.org 70 i 308 # TODO: Should this be match everything? Err bitreich.org 70 i 309 if len(line) == 0: Err bitreich.org 70 i 310 continue Err bitreich.org 70 i 311 if robotsmatch(line, selector) == True: Err bitreich.org 70 i 312 #print("isallowed = True") Err bitreich.org 70 i 313 isallowed = True Err bitreich.org 70 i 314 Err bitreich.org 70 i 315 #print("isallowed = %d" % (isallowed)) Err bitreich.org 70 i 316 return isallowed Err bitreich.org 70 i 317 Err bitreich.org 70 i 318 def loadselectorstxt(filename): Err bitreich.org 70 i 319 selectors = [] Err bitreich.org 70 i 320 Err bitreich.org 70 i 321 if os.path.exists(filename): Err bitreich.org 70 i 322 fd = open(filename, "r") Err bitreich.org 70 i 323 for line in fd: Err bitreich.org 70 i 324 fields = line.split("|") Err bitreich.org 70 i 325 selectors.append(fields) Err bitreich.org 70 i 326 fd.close() Err bitreich.org 70 i 327 Err bitreich.org 70 i 328 return selectors Err bitreich.org 70 i 329 Err bitreich.org 70 i 330 def loadlist(filename): Err bitreich.org 70 i 331 listelems = [] Err bitreich.org 70 i 332 Err bitreich.org 70 i 333 if os.path.exists(filename): Err bitreich.org 70 i 334 fd = open(filename, "r") Err bitreich.org 70 i 335 for line in fd: Err bitreich.org 70 i 336 line = line.strip() Err bitreich.org 70 i 337 if len(line) == 0: Err bitreich.org 70 i 338 continue Err bitreich.org 70 i 339 if line[0] == "#": Err bitreich.org 70 i 340 continue Err bitreich.org 70 i 341 listelems.append(line) Err bitreich.org 70 i 342 fd.close() Err bitreich.org 70 i 343 Err bitreich.org 70 i 344 return listelems Err bitreich.org 70 i 345 Err bitreich.org 70 i 346 def loadlistdb(filename): Err bitreich.org 70 i 347 listelems = [] Err bitreich.org 70 i 348 Err bitreich.org 70 i 349 if os.path.exists(filename): Err bitreich.org 70 i 350 fd = open(filename, "rb") Err bitreich.org 70 i 351 try: Err bitreich.org 70 i 352 listelems = pickle.load(fd) Err bitreich.org 70 i 353 except EOFError: Err bitreich.org 70 i 354 return [] Err bitreich.org 70 i 355 fd.close() Err bitreich.org 70 i 356 Err bitreich.org 70 i 357 return listelems Err bitreich.org 70 i 358 Err bitreich.org 70 i 359 def storelistdb(filename, listelems): Err bitreich.org 70 i 360 fd = mkopen(filename) Err bitreich.org 70 i 361 pickle.dump(listelems, fd) Err bitreich.org 70 i 362 fd.close() Err bitreich.org 70 i 363 Err bitreich.org 70 i 364 def storerawdata(cachedir, uri, data, host=None, port=70): Err bitreich.org 70 i 365 if host == None: Err bitreich.org 70 i 366 (host, port, mtype, selector) = parseuri(uri) Err bitreich.org 70 i 367 port = int(port) Err bitreich.org 70 i 368 Err bitreich.org 70 i 369 cachepath = "%s/%s:%s" % (cachedir, host, port) Err bitreich.org 70 i 370 mkpath(cachepath) Err bitreich.org 70 i 371 Err bitreich.org 70 i 372 m = hashlib.sha256() Err bitreich.org 70 i 373 m.update(uri.encode()) Err bitreich.org 70 i 374 urihash = m.hexdigest() Err bitreich.org 70 i 375 Err bitreich.org 70 i 376 cachepath = "%s/%s.menu" % (cachepath, urihash) Err bitreich.org 70 i 377 fd = mkopen(cachepath) Err bitreich.org 70 i 378 #print("Storing %s at %s" % (uri, cachepath)) Err bitreich.org 70 i 379 fd.write(("%s\n" % (uri)).encode()) Err bitreich.org 70 i 380 fd.write(data.encode()) Err bitreich.org 70 i 381 fd.close() Err bitreich.org 70 i 382 Err bitreich.org 70 i 383 def usage(app): Err bitreich.org 70 i 384 app = os.path.basename(app) Err bitreich.org 70 i 385 print("usage: %s [-hor] [-b base] [-f blocklist] [-w n] [starturl]" % (app), file=sys.stderr) Err bitreich.org 70 i 386 sys.exit(1) Err bitreich.org 70 i 387 Err bitreich.org 70 i 388 def main(args): Err bitreich.org 70 i 389 try: Err bitreich.org 70 i 390 opts, largs = getopt.getopt(args[1:], "hb:f:ow:r") Err bitreich.org 70 i 391 except getopt.GetoptError as err: Err bitreich.org 70 i 392 print(str(err)) Err bitreich.org 70 i 393 usage(args[0]) Err bitreich.org 70 i 394 Err bitreich.org 70 i 395 blocklistfile = None Err bitreich.org 70 i 396 blocklist = [] Err bitreich.org 70 i 397 Err bitreich.org 70 i 398 base = "." Err bitreich.org 70 i 399 starturi = None Err bitreich.org 70 i 400 workernum = 1 Err bitreich.org 70 i 401 robotscache = {} Err bitreich.org 70 i 402 forcehostscount = False Err bitreich.org 70 i 403 for o, a in opts: Err bitreich.org 70 i 404 if o == "-h": Err bitreich.org 70 i 405 usage(args[0]) Err bitreich.org 70 i 406 elif o == "-b": Err bitreich.org 70 i 407 base = a Err bitreich.org 70 i 408 elif o == "-f": Err bitreich.org 70 i 409 blocklistfile = a Err bitreich.org 70 i 410 blocklist = loadlist(blocklistfile) Err bitreich.org 70 i 411 print("blocklist: %s" % (blocklist)) Err bitreich.org 70 i 412 elif o == "-o": Err bitreich.org 70 i 413 forcehostscount = True Err bitreich.org 70 i 414 elif o == "-r": Err bitreich.org 70 i 415 # Do not cache robots.txt in memory. Err bitreich.org 70 i 416 robotscache = None Err bitreich.org 70 i 417 elif o == "-w": Err bitreich.org 70 i 418 try: Err bitreich.org 70 i 419 workernum = int(a) Err bitreich.org 70 i 420 except ValueError: Err bitreich.org 70 i 421 workernum = 1 Err bitreich.org 70 i 422 else: Err bitreich.org 70 i 423 assert False, "unhandled option" Err bitreich.org 70 i 424 Err bitreich.org 70 i 425 os.chdir(base) Err bitreich.org 70 i 426 cachedir = "%s/cache" % (base) Err bitreich.org 70 i 427 Err bitreich.org 70 i 428 if len(largs) > 0: Err bitreich.org 70 i 429 starturi = largs[0] Err bitreich.org 70 i 430 Err bitreich.org 70 i 431 knownuris = loadlistdb("knownuris.pickle") Err bitreich.org 70 i 432 if knownuris == []: Err bitreich.org 70 i 433 knownuris = {} Err bitreich.org 70 i 434 lastlenknownuris = len(knownuris) Err bitreich.org 70 i 435 Err bitreich.org 70 i 436 def isblocked(uri): Err bitreich.org 70 i 437 for rule in blocklist: Err bitreich.org 70 i 438 if uri.startswith(rule): Err bitreich.org 70 i 439 return True Err bitreich.org 70 i 440 return False Err bitreich.org 70 i 441 Err bitreich.org 70 i 442 def addhostscount(host): Err bitreich.org 70 i 443 if host in hostscount: Err bitreich.org 70 i 444 hostscount[host] += 1 Err bitreich.org 70 i 445 else: Err bitreich.org 70 i 446 hostscount[host] = 1 Err bitreich.org 70 i 447 Err bitreich.org 70 i 448 def subhostscount(host): Err bitreich.org 70 i 449 if host in hostscount: Err bitreich.org 70 i 450 hostscount[host] -= 1 Err bitreich.org 70 i 451 if hostscount[host] <= 0: Err bitreich.org 70 i 452 del hostscount[host] Err bitreich.org 70 i 453 Err bitreich.org 70 i 454 def addhostscache(uri, host=None, port=70, selector="/"): Err bitreich.org 70 i 455 if uri != None and host == None: Err bitreich.org 70 i 456 (host, port, mtype, selector) = parseuri(uri) Err bitreich.org 70 i 457 port = int(port) Err bitreich.org 70 i 458 else: Err bitreich.org 70 i 459 try: Err bitreich.org 70 i 460 port = int(port) Err bitreich.org 70 i 461 except ValueError: Err bitreich.org 70 i 462 return Err bitreich.org 70 i 463 Err bitreich.org 70 i 464 if uri in knownuris: Err bitreich.org 70 i 465 print("ignored for queue: %s" % (uri)) Err bitreich.org 70 i 466 return Err bitreich.org 70 i 467 if host == "": Err bitreich.org 70 i 468 print("ignored for queue: %s" % (uri)) Err bitreich.org 70 i 469 return Err bitreich.org 70 i 470 if isblocked(uri): Err bitreich.org 70 i 471 print("blocked by filters: %s" % (uri)) Err bitreich.org 70 i 472 return Err bitreich.org 70 i 473 Err bitreich.org 70 i 474 addhostscount(host) Err bitreich.org 70 i 475 Err bitreich.org 70 i 476 if not host in hostscache: Err bitreich.org 70 i 477 hostscache[host] = {} Err bitreich.org 70 i 478 if not "queue" in hostscache[host]: Err bitreich.org 70 i 479 hostscache[host]["queue"] = {} Err bitreich.org 70 i 480 Err bitreich.org 70 i 481 filterrules = cacherobots(cachedir, uri, \ Err bitreich.org 70 i 482 host=host, \ Err bitreich.org 70 i 483 port=port, \ Err bitreich.org 70 i 484 filtercache=robotscache) Err bitreich.org 70 i 485 if selectorisallowed(filterrules, selector) == True: Err bitreich.org 70 i 486 hostscache[host]["queue"][uri] = None Err bitreich.org 70 i 487 print("pushed to queue: %s" % (uri)) Err bitreich.org 70 i 488 else: Err bitreich.org 70 i 489 pass Err bitreich.org 70 i 490 print("blocked by robots: %s" % (uri)) Err bitreich.org 70 i 491 Err bitreich.org 70 i 492 def getqueuelen(): Err bitreich.org 70 i 493 queuelen = 0 Err bitreich.org 70 i 494 for host in hostscache: Err bitreich.org 70 i 495 queuelen += len(hostscache[host]["queue"]) Err bitreich.org 70 i 496 return queuelen Err bitreich.org 70 i 497 Err bitreich.org 70 i 498 hostscache = loadlistdb("hostscache.pickle") Err bitreich.org 70 i 499 if hostscache == []: Err bitreich.org 70 i 500 hostscache = {} Err bitreich.org 70 i 501 hostscount = loadlistdb("hostscount.pickle") Err bitreich.org 70 i 502 if hostscount == [] or forcehostscount == True: Err bitreich.org 70 i 503 hostscount = {} Err bitreich.org 70 i 504 for host in list(hostscache.keys()): Err bitreich.org 70 i 505 print("host = %s, queuelen = %d" \ Err bitreich.org 70 i 506 % (host, \ Err bitreich.org 70 i 507 len(hostscache[host]["queue"]))) Err bitreich.org 70 i 508 if len(hostscache[host]["queue"]) == 0: Err bitreich.org 70 i 509 del hostscache[host] Err bitreich.org 70 i 510 continue Err bitreich.org 70 i 511 for uri in hostscache[host]["queue"]: Err bitreich.org 70 i 512 (host, port, mtype, selector) = parseuri(uri) Err bitreich.org 70 i 513 addhostscount(host) Err bitreich.org 70 i 514 Err bitreich.org 70 i 515 def storestate(): Err bitreich.org 70 i 516 if blocklistfile != None: Err bitreich.org 70 i 517 blocklist = loadlist(blocklistfile) Err bitreich.org 70 i 518 if len(blocklist) > 0: Err bitreich.org 70 i 519 print("blocklist: %s" % (blocklist)) Err bitreich.org 70 i 520 print("################## Storing state to disc.") Err bitreich.org 70 i 521 storelistdb("knownuris.pickle", knownuris) Err bitreich.org 70 i 522 storelistdb("hostscache.pickle", hostscache) Err bitreich.org 70 i 523 storelistdb("hostscount.pickle", hostscount) Err bitreich.org 70 i 524 print("################## Storing state to disc done.") Err bitreich.org 70 i 525 Err bitreich.org 70 i 526 jobs = [] Err bitreich.org 70 i 527 if starturi != None: Err bitreich.org 70 i 528 #print("starturi = %s" % (starturi)) Err bitreich.org 70 i 529 if not isblocked(starturi): Err bitreich.org 70 i 530 (starthost, startport, startmtype, startselector) = parseuri(starturi) Err bitreich.org 70 i 531 addhostscache(starturi, \ Err bitreich.org 70 i 532 selector=startselector, \ Err bitreich.org 70 i 533 host=starthost, \ Err bitreich.org 70 i 534 port=startport) Err bitreich.org 70 i 535 try: Err bitreich.org 70 i 536 jobs.append([starturi, starthost, int(startport), startselector]) Err bitreich.org 70 i 537 except ValueError: Err bitreich.org 70 i 538 # Please fix your URI. Err bitreich.org 70 i 539 pass Err bitreich.org 70 i 540 Err bitreich.org 70 i 541 # Store state keeper. Err bitreich.org 70 i 542 startnow = datetime.now() Err bitreich.org 70 i 543 storedelta = timedelta(seconds=10) # 30 seconds Err bitreich.org 70 i 544 Err bitreich.org 70 i 545 lastlenknownhosts = len(hostscache) Err bitreich.org 70 i 546 lastlenuriqueue = getqueuelen() Err bitreich.org 70 i 547 while lastlenuriqueue > 0: Err bitreich.org 70 i 548 if len(jobs) < workernum: Err bitreich.org 70 i 549 for host in list(hostscache.keys()): Err bitreich.org 70 i 550 if len(hostscache[host]["queue"]) == 0: Err bitreich.org 70 i 551 del hostscache[host] Err bitreich.org 70 i 552 if host in hostscount: Err bitreich.org 70 i 553 del hostscount[host] Err bitreich.org 70 i 554 Err bitreich.org 70 i 555 selhosts = sorted(hostscount.items(), \ Err bitreich.org 70 i 556 key=operator.itemgetter(1))[:workernum*2] Err bitreich.org 70 i 557 Err bitreich.org 70 i 558 # Give hosts with many selectors more jobs. Err bitreich.org 70 i 559 hostjobs = {} Err bitreich.org 70 i 560 for selhost in selhosts: Err bitreich.org 70 i 561 # 10 ** x Err bitreich.org 70 i 562 hostjobs[selhost[0]] = \ Err bitreich.org 70 i 563 math.floor(math.log10(selhost[1])) Err bitreich.org 70 i 564 if hostjobs[selhost[0]] == 0: Err bitreich.org 70 i 565 hostjobs[selhost[0]] = 1 Err bitreich.org 70 i 566 print("Queue Status: %s" % (hostjobs)) Err bitreich.org 70 i 567 Err bitreich.org 70 i 568 for selhost in selhosts: Err bitreich.org 70 i 569 selhost = selhost[0] Err bitreich.org 70 i 570 seluris = hostscache[selhost]["queue"] Err bitreich.org 70 i 571 while hostjobs[selhost] > 0: Err bitreich.org 70 i 572 if len(seluris) == 0: Err bitreich.org 70 i 573 break Err bitreich.org 70 i 574 jobitem = seluris.popitem() Err bitreich.org 70 i 575 if isblocked(jobitem[0]): Err bitreich.org 70 i 576 continue Err bitreich.org 70 i 577 (host, port, mtype, selector) = parseuri(jobitem[0]) Err bitreich.org 70 i 578 job = [jobitem[0], host, port, selector] Err bitreich.org 70 i 579 if job not in jobs: Err bitreich.org 70 i 580 jobs.append([jobitem[0], host, port, selector]) Err bitreich.org 70 i 581 hostjobs[selhost] -= 1 Err bitreich.org 70 i 582 Err bitreich.org 70 i 583 print("Getting %d jobs." % (len(jobs))) Err bitreich.org 70 i 584 Err bitreich.org 70 i 585 dataresults = [] Err bitreich.org 70 i 586 with Pool(processes=workernum) as pool: Err bitreich.org 70 i 587 dataresults = pool.map(poolgopher, jobs) Err bitreich.org 70 i 588 #data = gopher(host=host, port=port, selector=selector) Err bitreich.org 70 i 589 jobs = [] Err bitreich.org 70 i 590 Err bitreich.org 70 i 591 for dataresult in dataresults: Err bitreich.org 70 i 592 (cururi, host, port, selector, data) = dataresult Err bitreich.org 70 i 593 subhostscount(host) Err bitreich.org 70 i 594 storerawdata(cachedir, cururi, data, host=host, port=port) Err bitreich.org 70 i 595 menudata = parsemenu(data) Err bitreich.org 70 i 596 #print(menudata) Err bitreich.org 70 i 597 for mi in menudata: Err bitreich.org 70 i 598 # Only menus so far. Err bitreich.org 70 i 599 if mi[0] == "1": Err bitreich.org 70 i 600 # Fix menu items with ports in hosts. Err bitreich.org 70 i 601 if ":" in mi[3]: Err bitreich.org 70 i 602 mi[3] = mi[3].split(":")[0] Err bitreich.org 70 i 603 Err bitreich.org 70 i 604 guri = "gopher://%s:%s/%s%s" % \ Err bitreich.org 70 i 605 (mi[3], mi[4], mi[0], mi[2]) Err bitreich.org 70 i 606 Err bitreich.org 70 i 607 addhostscache(guri, host=mi[3], \ Err bitreich.org 70 i 608 port=mi[4], \ Err bitreich.org 70 i 609 selector=mi[2]) Err bitreich.org 70 i 610 Err bitreich.org 70 i 611 print("Uri %s done." % (cururi)) Err bitreich.org 70 i 612 knownuris[cururi] = None Err bitreich.org 70 i 613 Err bitreich.org 70 i 614 lenuriqueue = getqueuelen() Err bitreich.org 70 i 615 lenknownuris = len(knownuris) Err bitreich.org 70 i 616 lenknownhosts = len(hostscache) Err bitreich.org 70 i 617 print("> queue hosts = %d (%d) %s" % \ Err bitreich.org 70 i 618 (lenknownhosts, lenknownhosts - Err bitreich.org 70 i 619 lastlenknownhosts, hostscache.keys())) Err bitreich.org 70 i 620 print("> uri queue len = %d (%d)" % \ Err bitreich.org 70 i 621 (lenuriqueue, lenuriqueue - lastlenuriqueue)) Err bitreich.org 70 i 622 print("> visited uris = %d (%d)" % \ Err bitreich.org 70 i 623 (lenknownuris, lenknownuris - lastlenknownuris)) Err bitreich.org 70 i 624 lastlenknownuris = lenknownuris Err bitreich.org 70 i 625 lastlenuriqueue = lenuriqueue Err bitreich.org 70 i 626 lastlenknownhosts = lenknownhosts Err bitreich.org 70 i 627 Err bitreich.org 70 i 628 # TODO: Remove after debugging Err bitreich.org 70 i 629 nowdelta = datetime.now() - startnow Err bitreich.org 70 i 630 if nowdelta >= storedelta: Err bitreich.org 70 i 631 storestate() Err bitreich.org 70 i 632 startnow = datetime.now() Err bitreich.org 70 i 633 Err bitreich.org 70 i 634 time.sleep(0.2) # don't be too harsh on servers Err bitreich.org 70 i 635 Err bitreich.org 70 i 636 #break #oneshot Err bitreich.org 70 i 637 Err bitreich.org 70 i 638 # Save at end of even single shot. Err bitreich.org 70 i 639 storestate() Err bitreich.org 70 i 640 Err bitreich.org 70 i 641 return 0 Err bitreich.org 70 i 642 Err bitreich.org 70 i 643 if __name__ == "__main__": Err bitreich.org 70 i 644 sys.exit(main(sys.argv)) Err bitreich.org 70 i 645 Err bitreich.org 70 .