Sitemap generator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
5.2 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. import urllib
  2. from bs4 import BeautifulSoup
  3. import urlparse
  4. import mechanize
  5. import pickle
  6. import re
  7. try:
  8. import sys
  9. import gevent
  10. from gevent import monkey, pool, queue
  11. monkey.patch_all()
  12. if 'threading' in sys.modules:
  13. del sys.modules['threading']
  14. print('threading module loaded before patching!\n')
  15. print('threading module deleted from sys.modules!\n')
  16. gevent_installed = True
  17. except:
  18. print("Gevent does not installed. Parsing process will be slower.")
  19. gevent_installed = False
  20. class Crawler:
  21. def __init__(self, url, outputfile='sitemap.xml', logfile='error.log', oformat='xml'):
  22. self.url = url
  23. self.logfile = open(logfile, 'a')
  24. self.oformat = oformat
  25. self.outputfile = outputfile
  26. # create lists for the urls in que and visited urls
  27. self.urls = set([url])
  28. self.visited = set([])
  29. self.excepted = set([])
  30. self.exts = ['htm', 'php']
  31. self.allowed_regex = '\.((?!htm)(?!php)\w+)$'
  32. def set_exts(self, exts):
  33. self.exts = exts
  34. def allow_regex(self, regex=None):
  35. if not regex is None:
  36. self.allowed_regex = regex
  37. else:
  38. allowed_regex = ''
  39. for ext in self.exts:
  40. allowed_regex += '(!{})'.format(ext)
  41. self.allowed_regex = '\.({}\w+)$'.format(allowed_regex)
  42. def crawl(self, echo=False, pool_size=1):
  43. self.echo = echo
  44. self.regex = re.compile(self.allowed_regex)
  45. if gevent_installed and pool_size > 1:
  46. self.pool = pool.Pool(pool_size)
  47. self.queue = gevent.queue.Queue()
  48. self.queue.put(self.url)
  49. self.pool.spawn(self.parse_gevent)
  50. while not self.queue.empty() and not self.pool.free_count() == pool_size:
  51. gevent.sleep(0.1)
  52. while len(self.urls) > 0:
  53. self.queue.put(self.urls.pop())
  54. for x in xrange(0, min(self.queue.qsize(), self.pool.free_count())):
  55. self.pool.spawn(self.parse_gevent)
  56. self.pool.join()
  57. else:
  58. while len(self.urls) > 0:
  59. self.parse()
  60. if self.oformat == 'xml':
  61. self.write_xml()
  62. self.errlog()
  63. def parse_gevent(self):
  64. url = self.queue.get(timeout=0)
  65. try:
  66. br = mechanize.Browser()
  67. response = br.open(url)
  68. if response.code >= 400:
  69. self.excepted.update([(url, "Error {} at url {}".format(response.code, url))])
  70. return
  71. for link in br.links():
  72. newurl = urlparse.urljoin(link.base_url, link.url)
  73. if self.is_valid(newurl):
  74. self.visited.update([newurl])
  75. self.urls.update([newurl])
  76. except Exception, e:
  77. self.excepted.update([(url, e.message)])
  78. return
  79. except gevent.queue.Empty:
  80. br.close()
  81. if self.echo:
  82. print('{} pages parsed :: {} parsing processes :: {} pages in the queue'.format(len(self.visited), len(self.pool), self.queue.qsize()))
  83. return
  84. br.close()
  85. if self.echo:
  86. print('{} pages parsed :: {} parsing processes :: {} pages in the queue'.format(len(self.visited), len(self.pool), self.queue.qsize()))
  87. def parse(self):
  88. if self.echo:
  89. print('{} pages parsed :: {} pages in the queue'.format(len(self.visited), len(self.urls)))
  90. # Set the startingpoint for the spider and initialize
  91. # the a mechanize browser object
  92. url = self.urls.pop()
  93. br = mechanize.Browser()
  94. try:
  95. response = br.open(url)
  96. if response.code >= 400:
  97. self.excepted.update([(url, "Error {} at url {}".format(response.code, url))])
  98. return
  99. for link in br.links():
  100. newurl = urlparse.urljoin(link.base_url, link.url)
  101. #print newurl
  102. if self.is_valid(newurl):
  103. self.urls.update([newurl])
  104. self.visited.update([url])
  105. except Exception, e:
  106. self.excepted.update([(url, e.message)])
  107. br.close()
  108. del(br)
  109. def is_valid(self, url):
  110. valid = False
  111. if url in self.visited:
  112. return False
  113. if not self.url in url:
  114. return False
  115. if re.search(self.regex, url):
  116. return False
  117. return True
  118. def errlog(self):
  119. while len(self.excepted) > 0:
  120. ex = self.excepted.pop()
  121. self.logfile.write('{}\n'.format('\t'.join(ex)))
  122. self.logfile.close()
  123. def write_xml(self):
  124. of = open(self.outputfile, 'w')
  125. of.write('<?xml version="1.0" encoding="utf-8"?>\n')
  126. of.write('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">\n')
  127. url_str = '<url><loc>{}</loc></url>\n'
  128. while self.visited:
  129. of.write(url_str.format(self.visited.pop()))
  130. of.write('</urlset>')
  131. of.close()