这篇文章主要讲解 python如何实现简易爬虫
爬虫流程
打开种子url ——> 获取种子url页面中所有的url ——> 判断是否被爬取过,未爬取过的url添加到url列表中 ——> 解析页面中需要的信息 ——> 写入数据库
上述流程中可以抽象出5个对象:
- bootstrap 启动器
- downloader 下载器
- parser 解析器
- url_manager url管理器
- outputer 输出器
启动器(main.py)
先看启动器是如何实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
""" Created on 2017/9/23 @author: tt """
import spider_outputer import spider_parser
import spider_downloader import spider_url_manager
class Main: def __init__(self): self.urls = spider_url_manager.UrlManager() self.downloader = spider_downloader.Downloader() self.outputer = spider_outputer.Outputer() self.parser = spider_parser.Parser()
def craw(self, root_url): self.urls.add_new_url(root_url) count = 1 while self.urls.has_new_url(): try: new_url = self.urls.get_new_url() print('craw %d : %s' % (count, new_url)) html_content = self.downloader.download(new_url) new_urls, new_data = self.parser.parser(new_url, html_content) self.urls.add_new_urls(new_urls) self.outputer.collect_data(new_data) count += 1 except Exception as e: print(e)
if __name__ == "__main__": url = 'https://www.zhihu.com/people/li-xiao-miao-70/activities' main = Main() main.craw(url)
|
下载器(spider_downloader.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
""" Created on 2017/9/23 @author: tt """ import string from urllib import request from urllib.parse import quote
user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36'
class Downloader: def download(self, url): if url is None: return None _url = quote(url, safe=string.printable) req = request.Request(_url, headers={'User-Agent': user_agent}) response = request.urlopen(req) if response.getcode() != 200: print("request bad") return None html = response.read() return html.decode('utf8')
|
解析器(spider_parser.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
""" Created on 2017/9/23 @author: tt """ from urllib import parse
from bs4 import BeautifulSoup
import user_info
class Parser: def parser(self, page_url, html_content): if page_url is None or html_content is None: return soup = BeautifulSoup(html_content, "html.parser") new_urls = self._get_new_urls(page_url, soup) new_data = self._get_new_data(page_url, soup) return new_urls, new_data
def _get_new_urls(self, page_url, soup): new_urls = set() links = soup.find_all('a') for link in links: new_url = link.get('href') new_full_url = parse.urljoin(page_url, new_url) new_urls.add(new_full_url) return new_urls
def _get_new_data(self, page_url, soup): ret_data = [] author_infos = soup.find_all(class_='AuthorInfo') print(len(author_infos)) for author_info in author_infos: user_id = author_info.find(class_='UserLink-link').get('href') user_id = user_id[user_id.find('e/') + 2:] photo = author_info.find(class_='AuthorInfo-avatar').get('srcset') pos = photo.find(' ') photo = photo[:pos] name = author_info.find('meta', attrs={"itemprop": "name"}).get('content') profession = author_info.find(class_='AuthorInfo-badgeText').get_text()
user = user_info.UserInfo(user_id, name, photo, profession) ret_data.append(user)
return ret_data
|
url管理器(spider_url_manager.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
""" Created on 2017/9/23 @author: tt """
class UrlManager: def __init__(self): self.visited_url = set() self.visit_url = set()
def add_new_url(self, url): if url is None: return if url not in self.visit_url and url not in self.visited_url: self.visit_url.add(url)
def has_new_url(self): return len(self.visit_url) != 0
def add_new_urls(self, urls): if urls is None or len(urls) == 0: return for url in urls: self.add_new_url(url)
def get_new_url(self): new_url = self.visit_url.pop() self.visited_url.add(new_url) return new_url
|
输出器(spider_outputer.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
""" Created on 2017/9/23 @author: tt """ import user_dao
class Outputer: def __init__(self): self.user_dao = user_dao.UserDao()
def collect_data(self, data): if data is None: print('None') return else: for d in data: self.user_dao.add_user(d)
|
其他类
数据库帮助类(db_helper.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
""" Created on 2017/9/23 @author: tt """ import pymysql
class DbHelper: def _get_connection(self): return pymysql.Connect(host='127.0.0.1', user='root', passwd='root', db='crawler', port=3306, charset='utf8')
def execute_update(self, sql, params): connectin = self._get_connection() cursor = connectin.cursor() try: if params is None: s = sql else: s = (sql % params) result = cursor.execute(s) connectin.commit() print('rows:', result) except Exception as e: print(e) connectin.rollback() finally: cursor.close() connectin.close()
def query(self, sql, params=None): connection = self._get_connection() cursor = connection.cursor() try: if params is None: s = sql else: s = (sql % params) cursor.execute(s) rows = cursor.fetchall() for row in rows: print(row) except Exception as e: print(e) connection.rollback() finally: cursor.close() connection.close()
|
用户数据访问类(user_dao.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
""" Created on 2017/9/23 @author: tt """ import db_helper
class UserDao: def __init__(self): self.db = db_helper.DbHelper()
def add_user(self, user): sql = '''INSERT IGNORE INTO zhihu_user(user_id, photo, name, profession) values('%s', '%s', '%s', '%s')''' params = (user.user_id, user.photo, user.name, user.profession) self.db.execute_update(sql=sql, params=params)
|
用户信息类(user_info.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
""" Created on 2017/9/23 @author: tt """
class UserInfo: def __init__(self, user_id, name, photo, profession): self.user_id = user_id self.name = name self.photo = photo self.profession = profession
def __str__(self): return 'UserInfo(name=' + self.name + ', photo=' + self.photo + ", profession=" + self.profession + ')\n'
def __repr__(self): return self.__str__()
|
在python中print在输出对象是调用的是对象的_str_函数
这个小的demo爬取的是知乎用户id,头像,名称,职业信息
如果您觉得文章有用或对您有帮助,欢迎通过以下方式赞助我。 ♪(^∀^●)ノ