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.

140 lines
4.9 KiB

9 years ago
9 years ago
7 years ago
8 years ago
8 years ago
9 years ago
9 years ago
8 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
7 years ago
7 years ago
8 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
  1. # -*- coding: utf-8 -*-
  2. import __future__
  3. import sys
  4. import urlparse
  5. import requests
  6. from lxml import html
  7. import re
  8. try:
  9. import sys
  10. if 'threading' in sys.modules:
  11. del sys.modules['threading']
  12. print('threading module loaded before patching!')
  13. print('threading module deleted from sys.modules!\n')
  14. from gevent import monkey, pool
  15. monkey.patch_all()
  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([url])
  29. self.exts = ['htm', 'php']
  30. self.allowed_regex = '\.((?!htm)(?!php)\w+)$'
  31. self.errors = {'404': []}
  32. def set_exts(self, exts):
  33. self.exts = exts
  34. def allow_regex(self, regex=None):
  35. if regex is not 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.pool.spawn(self.parse_gevent)
  48. self.pool.join()
  49. else:
  50. while len(self.urls) > 0:
  51. self.parse()
  52. if self.oformat == 'xml':
  53. self.write_xml()
  54. elif self.oformat == 'txt':
  55. self.write_txt()
  56. with open('errors.txt', 'w') as err_file:
  57. for key, val in self.errors.items():
  58. err_file.write(u'\n\nError {}\n\n'.format(key))
  59. err_file.write(u'\n'.join(set(val)))
  60. def parse_gevent(self):
  61. self.parse()
  62. while len(self.urls) > 0 and not self.pool.full():
  63. self.pool.spawn(self.parse_gevent)
  64. def parse(self):
  65. if self.echo:
  66. if not gevent_installed:
  67. print('{} pages parsed :: {} pages in the queue'.format(len(self.visited), len(self.urls)))
  68. else:
  69. print('{} pages parsed :: {} parsing processes :: {} pages in the queue'.format(len(self.visited), len(self.pool), len(self.urls)))
  70. # Set the startingpoint for the spider and initialize
  71. # the a mechanize browser object
  72. if not self.urls:
  73. return
  74. else:
  75. url = self.urls.pop()
  76. try:
  77. response = requests.get(url)
  78. # if status code is not 404, then add url in seld.errors dictionary
  79. if response.status_code != 200:
  80. if self.errors.get(str(response.status_code), False):
  81. self.errors[str(response.status_code)].extend([url])
  82. else:
  83. self.errors.update({str(response.status_code): [url]})
  84. self.errlog("Error {} at url {}".format(response.status_code, url))
  85. return
  86. tree = html.fromstring(response.text)
  87. for link_tag in tree.findall('.//a'):
  88. link = link_tag.attrib.get('href', '')
  89. newurl = urlparse.urljoin(self.url, link)
  90. # print(newurl)
  91. if self.is_valid(newurl):
  92. self.visited.update([newurl])
  93. self.urls.update([newurl])
  94. except Exception, e:
  95. self.errlog(e.message)
  96. def is_valid(self, url):
  97. if '#' in url:
  98. url = url[:url.find('#')]
  99. if url in self.visited:
  100. return False
  101. if self.url not in url:
  102. return False
  103. if re.search(self.regex, url):
  104. return False
  105. return True
  106. def errlog(self, msg):
  107. self.logfile.write(msg)
  108. self.logfile.write('\n')
  109. def write_xml(self):
  110. of = open(self.outputfile, 'w')
  111. of.write('<?xml version="1.0" encoding="utf-8"?>\n')
  112. 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')
  113. url_str = '<url><loc>{}</loc></url>\n'
  114. while self.visited:
  115. of.write(url_str.format(self.visited.pop()))
  116. of.write('</urlset>')
  117. of.close()
  118. def write_txt(self):
  119. of = open(self.outputfile, 'w')
  120. url_str = '{}\n'
  121. while self.visited:
  122. of.write(url_str.format(self.visited.pop()))
  123. of.close()