[关闭]
@wade123 2019-05-07T01:33:27.000000Z 字数 7134 阅读 943

10-1:从 def 函数到 Class 再到 Scrapy

Python入门爬虫与数据分析


摘要:实例对比函数和类的代码写法。

不知道你注意到没有,我们之前使用的两个爬虫框架:Pyspider 和 Scrapy 都用到了类(Class)的代码编写方法。而此前我们一直使用函数(def)的写法。接触类(Class)的用法时,可能会有点不习惯,也会产生「有了函数为什么还需要类」的疑问。不过,面向对象编程是 Python 最重要的思想,类(Class)又是面向对象最重要的概念之一,所以要想精通 Python ,则必须得会使用类(Class)来编写代码,所以下面来通过价格实例,帮助快速从函数的写法顺利过渡到类的代码编写方式。

关于类(Class)的教程,网上主要有两类,一类是廖雪峰大佬的,另一类是不加说明默认你已经会这种写法,而直接使用的。

廖雪峰的教程非常棒,但是更适合入门 Python 有一段时间或者看过一些更基础的教程之后再回过头来看,否则可能会觉得他的教程理论性重于实用性。我第一次看了他的教程中关于类的相关知识后,觉得理解了,但一尝试自己写时就不太会了。

第二类教程,网上有很多案例,这类教程存在的问题就是,你能看懂意思,但还是不太会运用到自己的案例中。

总结一下这两类教程对新手不友好的地方就是 没有同时给出两种写法的实例,这就没办法对比,而「对比学习」是一种学习新知识非常快的途径,简单来说就是学新知识的时候,先从我们已经掌握的知识出发,和新知识进行对比,快速找到共同点和不同点,共同点我们能很快掌握,针对不同点通过对比去感悟领会,从而快速学会新知识。

接下来,就举几个同时使用了函数写法和类的写法的案例,希望能够帮助你快速完成从函数到类的编程思想的过渡转换。

▌爬取豆瓣电影 TOP250

第一个案例:爬取豆瓣电影 TOP250,我们的目标是通过调用豆瓣 API 接口,获取电影名称、评分、演员等信息,然后存储到 CSV 文件中,部分代码如下:

  1. def get_content(start_page):
  2. url = 'https://api.douban.com/v2/movie/top250?'
  3. params = {
  4. 'start':start_page,
  5. 'count':50
  6. }
  7. response = requests.get(url,params=params).json()
  8. movies = response['subjects']
  9. data = [{
  10. 'rating':item['rating']['average'],
  11. 'genres':item['genres'],
  12. 'name':item['title'],
  13. 'actor':get_actor(item['casts']),
  14. 'original_title':item['original_title'],
  15. 'year':item['year'],
  16. } for item in movies]
  17. write_to_file(data)
  18. def get_actor(actors):
  19. actor = [i['name'] for i in actors]
  20. return actor
  21. def write_to_file(data):
  22. with open('douban_def.csv','a',encoding='utf_8_sig',newline='') as f:
  23. w = csv.writer(f)
  24. for item in data:
  25. w.writerow(item.values())
  26. def get_douban(total_movie):
  27. # 每页50条,start_page循环5次
  28. for start_page in range(0,total_movie,50):
  29. get_content(start_page)
  30. if __name__ == '__main__':
  31. get_douban(250)

打开 CSV 文件查看输出的结果:

以上,我们通过四个函数就完成了数据的爬取和存储,逻辑很清晰,下面我们使用类的写法实现同样的功能,部分代码如下:

  1. class Douban(object):
  2. def __init__(self):
  3. self.url = 'https://api.douban.com/v2/movie/top250?'
  4. def get_content(self,start_page):
  5. params = {
  6. 'start':start_page,
  7. 'count':50
  8. }
  9. response = requests.get(self.url,params=params).json()
  10. movies = response['subjects']
  11. data = [{
  12. 'rating':item['rating']['average'],
  13. 'genres':item['genres'],
  14. 'name':item['title'],
  15. 'actor':self.get_actor(item['casts']),
  16. 'original_title':item['original_title'],
  17. 'year':item['year'],
  18. } for item in movies]
  19. self.write_to_file(data)
  20. def get_actor(self,actors):
  21. actor = [i['name'] for i in actors]
  22. return actor
  23. def write_to_file(self,data):
  24. with open('douban_class.csv','a',encoding='utf_8_sig',newline='') as f:
  25. w = csv.writer(f)
  26. for item in data:
  27. w.writerow(item.values())
  28. def get_douban(self,total_movie):
  29. # 每页50条,start_page循环5次
  30. for start_page in range(0,total_movie,50):
  31. self.get_content(start_page)
  32. if __name__ == '__main__':
  33. douban = Douban()
  34. douban.get_douban(250)

可以看到,上面的案例中,类的写法和函数的写法大部分都是一样的,仅少数部分存在差异。主要有这么几点差异:

下面,我们再来看看第二个例子。

▌模拟登陆 IT 桔子

我们使用 Selenium 模拟登陆 IT 桔子网并输出网页源码,使用函数的部分代码如下:

  1. def login():
  2. browser = webdriver.Chrome()
  3. browser.get('https://www.itjuzi.com/user/login')
  4. account = browser.find_element(By.ID, "create_account_email")
  5. password = browser.find_element(By.ID, "create_account_password")
  6. account.send_keys('irw27812@awsoo.com') # 输入账号和密码
  7. password.send_keys('test2018') # 输入账号密码
  8. submit = browser.find_element(By.ID,"login_btn")
  9. submit.click() # 点击登录按钮
  10. get_content()
  11. def get_content():
  12. browser.get('http://radar.itjuzi.com/investevent')
  13. print(browser.page_source) # 输出网页源码
  14. if __name__ == '__main__':
  15. login()

以上代码实现了自动输入账号密码然后进入 IT 桔子网,下面我们再来看看类的写法:

  1. class Spider(object):
  2. def __init__(self,account,password):
  3. self.login_url = 'https://www.itjuzi.com/user/login'
  4. self.get_url = 'http://radar.itjuzi.com/investevent'
  5. self.account = account
  6. self.password = password
  7. def login(self):
  8. browser.get(self.login_url)
  9. account = browser.find_element(By.ID, "create_account_email")
  10. password = browser.find_element(By.ID, "create_account_password")
  11. account.send_keys(self.account) # 输入账号和密码
  12. password.send_keys(self.password)
  13. submit = browser.find_element(By.ID,"login_btn")
  14. submit.click() # 点击登录按钮
  15. self.get_content() # 调用下面的方法
  16. def get_content(self):
  17. browser.get(self.get_url)
  18. print(browser.page_source) # 输出网页源码
  19. if __name__ == '__main__':
  20. spider = Spider(account='irw27812@awsoo.com',password='test2018')
  21. # 当有其他账号时,在这里更改即可,很方便
  22. # spider = Spider('fru68354@nbzmr.com','test2018')
  23. spider.login()

这里,我们将一些固定的参数,比如 URL、Headers 都放在 __init__ 方法中,需要的时候在各个函数中进行调用,这样的写法逻辑更加清晰。

下面,我们再看看第三个例子爬取虎嗅文章,从普通类的写法过渡到 pyspider 框架中类的写法,这样有助于快速上手 pyspider 框架。

▌爬取虎嗅文章

我们目标是通过分析 AJAX 请求,遍历爬取虎嗅网的文章信息,先来看看普通类的写法,部分代码如下:

  1. client = pymongo.MongoClient('localhost',27017)
  2. db = client.Huxiu
  3. mongo_collection = db.huxiu_news
  4. class Huxiu(object):
  5. def __init__(self):
  6. self.headers = {
  7. 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
  8. 'X-Requested-With': 'XMLHttpRequest'
  9. }
  10. self.url = 'https://www.huxiu.com/v2_action/article_list'
  11. def get_content(self,page):
  12. data = {
  13. 'page': page,
  14. }
  15. response = requests.post(self.url,data=data,headers=self.headers)
  16. self.parse_content(response)
  17. def parse_content(self,response):
  18. content = response.json()['data']
  19. doc = pq(content)
  20. lis = doc('.mod-art').items()
  21. data =[{
  22. 'title': item('.msubstr-row2').text(),
  23. 'url':'https://www.huxiu.com'+ str(item('.msubstr-row2').attr('href')),
  24. 'name': item('.author-name').text(),
  25. 'write_time':item('.time').text(),
  26. 'comment':item('.icon-cmt+ em').text(),
  27. 'favorites':item('.icon-fvr+ em').text(),
  28. 'abstract':item('.mob-sub').text()
  29. } for item in lis]
  30. self.save_to_file(data)
  31. # 存储到 mongodb
  32. def save_to_file(self,data):
  33. df = pd.DataFrame(data)
  34. content = json.loads(df.T.to_json()).values()
  35. if mongo_collection.insert_many(content):
  36. print('存储到 mongondb 成功')
  37. else:
  38. print('存储失败')
  39. def get_huxiu(self,start_page,end_page):
  40. for page in range(start_page,end_page) :
  41. print('正在爬取第 %s 页' % page)
  42. self.get_content(page)
  43. if __name__ == '__main__':
  44. huxiu = Huxiu()
  45. huxiu.get_huxiu(1,2000)

然后再看看在 Pyspider 中的写法:

  1. class Handler(BaseHandler):
  2. crawl_config:{
  3. "headers":{
  4. 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
  5. 'X-Requested-With': 'XMLHttpRequest'
  6. }
  7. }
  8. # 修改taskid,避免只下载一个post请求
  9. def get_taskid(self,task):
  10. return md5string(task['url']+json.dumps(task['fetch'].get('data','')))
  11. def on_start(self):
  12. for page in range(1,2000):
  13. print('正在爬取第 %s 页' % page)
  14. self.crawl('https://www.huxiu.com/v2_action/article_list',method='POST',data={'page':page}, callback=self.index_page)
  15. def index_page(self, response):
  16. content = response.json['data']
  17. # 注意,在上面的class写法中,json后面需要添加(),pyspider中则不用
  18. doc = pq(content)
  19. lis = doc('.mod-art').items()
  20. data = [{
  21. 'title': item('.msubstr-row2').text(),
  22. 'url':'https://www.huxiu.com'+ str(item('.msubstr-row2').attr('href')),
  23. 'name': item('.author-name').text(),
  24. 'write_time':item('.time').text(),
  25. 'comment':item('.icon-cmt+ em').text(),
  26. 'favorites':item('.icon-fvr+ em').text(),
  27. 'abstract':item('.mob-sub').text()
  28. } for item in lis ]
  29. return data
  30. def on_result(self,result):
  31. if result:
  32. self.save_to_mongo(result)
  33. def save_to_mongo(self,result):
  34. df = pd.DataFrame(result)
  35. content = json.loads(df.T.to_json()).values()
  36. if mongo_collection.insert_many(content):
  37. print('存储到 mongondb 成功')

可以看到,pyspider 中主体部分和普通类的写法差不多,不同的地方在于 pyspider 中有一些固定的语法,这可以通过参考 pyspider 教程快速掌握,Scrapy 框架也类似。

通过以上三个例子的对比,相信你可以感受到函数(def)、 类(Class)和 爬虫框架三种写法的异同点。

文中完整代码可以在下方链接中得到:

https://github.com/makcyun/web_scraping_with_python/tree/master/%E4%BB%8Edef%E5%88%B0class%E5%86%8D%E5%88%B0scrapy

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注