存储数据

存储海量数据

数据持久化的首选方案应该是关系型数据库,关系型数据库的产品很多,包括:Oracle、MySQL、SQLServer、PostgreSQL等。如果要存储海量的低价值数据,文档数据库也是不错的选择,MongoDB是文档数据库中的佼佼者,有兴趣的读者可以自行研究。

下面的代码演示了如何使用MySQL来保存从知乎发现上爬取到的链接和页面。

  1. create database zhihu default charset utf8;
  2. create user 'hellokitty'@'%' identified by 'Hellokitty.618';
  3. grant all privileges on zhihu.* to 'hellokitty'@'%';
  4. flush privileges;
  5. use zhihu;
  6. create table `tb_explore`
  7. (
  8. `id` integer auto_increment,
  9. `url` varchar(1024) not null,
  10. `page` longblob not null,
  11. `digest` char(48) unique not null,
  12. `idate` datetime default now(),
  13. primary key (`id`)
  14. );
  1. import hashlib
  2. import pickle
  3. import re
  4. import zlib
  5. from urllib.parse import urljoin
  6. import MySQLdb
  7. import bs4
  8. import requests
  9. conn = MySQLdb.connect(host='1.2.3.4', port=3306,
  10. user='hellokitty', password='Hellokitty.618',
  11. database='zhihu', charset='utf8',
  12. autocommit=True)
  13. def write_to_db(url, page, digest):
  14. try:
  15. with conn.cursor() as cursor:
  16. cursor.execute(
  17. 'insert into tb_explore (url, page, digest) values (%s, %s, %s) ',
  18. (url, page, digest)
  19. )
  20. except MySQLdb.MySQLError as err:
  21. print(err)
  22. def main():
  23. base_url = 'https://www.zhihu.com/'
  24. seed_url = urljoin(base_url, 'explore')
  25. headers = {'user-agent': 'Baiduspider'}
  26. try:
  27. resp = requests.get(seed_url, headers=headers)
  28. soup = bs4.BeautifulSoup(resp.text, 'lxml')
  29. href_regex = re.compile(r'^/question')
  30. for a_tag in soup.find_all('a', {'href': href_regex}):
  31. href = a_tag.attrs['href']
  32. full_url = urljoin(base_url, href)
  33. digest = hashlib.sha1(full_url.encode()).hexdigest()
  34. html_page = requests.get(full_url, headers=headers).text
  35. zipped_page = zlib.compress(pickle.dumps(html_page))
  36. write_to_db(full_url, zipped_page, digest)
  37. finally:
  38. conn.close()
  39. if __name__ == '__main__':
  40. main()

数据缓存

通过《网络数据采集和解析》一文,我们已经知道了如何从指定的页面中抓取数据,以及如何保存抓取的结果,但是我们没有考虑过这么一种情况,就是我们可能需要从已经抓取过的页面中提取出更多的数据,重新去下载这些页面对于规模不大的网站倒是问题也不大,但是如果能够把这些页面缓存起来,对应用的性能会有明显的改善。下面的例子演示了如何使用Redis来缓存知乎发现上的页面。

  1. import hashlib
  2. import pickle
  3. import re
  4. import zlib
  5. from urllib.parse import urljoin
  6. import bs4
  7. import redis
  8. import requests
  9. def main():
  10. base_url = 'https://www.zhihu.com/'
  11. seed_url = urljoin(base_url, 'explore')
  12. client = redis.Redis(host='1.2.3.4', port=6379, password='1qaz2wsx')
  13. headers = {'user-agent': 'Baiduspider'}
  14. resp = requests.get(seed_url, headers=headers)
  15. soup = bs4.BeautifulSoup(resp.text, 'lxml')
  16. href_regex = re.compile(r'^/question')
  17. for a_tag in soup.find_all('a', {'href': href_regex}):
  18. href = a_tag.attrs['href']
  19. full_url = urljoin(base_url, href)
  20. field_key = hashlib.sha1(full_url.encode()).hexdigest()
  21. if not client.hexists('spider:zhihu:explore', field_key):
  22. html_page = requests.get(full_url, headers=headers).text
  23. zipped_page = zlib.compress(pickle.dumps(html_page))
  24. client.hset('spider:zhihu:explore', field_key, zipped_page)
  25. print('Total %d question pages found.' % client.hlen('spider:zhihu:explore'))
  26. if __name__ == '__main__':
  27. main()