一、前言

  本文适合有一定Python基础的同学学习Python爬虫,无基础请点击:慕课网——Python入门
  申明:实例的主体框架来自于慕课网——Python开发简单爬虫

  语言:Python2

  IDE:VScode

二、何为爬虫

  传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,然后下载队列中的URL地址对应的网页。解析后抓取网页内容,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。同时,它还会根据一定的搜索策略获取我们所需的信息并保存下来。最后为了展示我们爬到的数据,往往还会用HTML的表格或记事本保存我们所需要的数据。
  简单来说,爬虫就是一门用来从互联网上自动获取我们所需数据的技术。

三、JD商品详情页的网页分析

  入口URL选择为JD某商品详情页:https://item.jd.com/4224129.html

  我们需要分析的内容主要有:

  3.1详情页上指向的其他URL

    我们打开https://item.jd.com/4224129.html,发现网页上还有很多指向其他商品的链接。

    

    

    通过鼠标右键,查看元素,我们可以发现商品页面上的以上链接均为以下格式: //item.jd.com/数字.html

    

    分析到此,我们就知道抓取网页内容时,从当前页面上抽取新的URL的方法了。

 

  3.2商品名称、价格

  同理,我们在商品名称和价格处点击鼠标右键查看元素

  

      

四、简单爬虫框架

  1.爬虫总调度程序

    即我们的main文件,以入口URL为参数爬取所有相关页面
    
  2.URL管理器

    维护待爬取和已爬取的URL列表

  3.HTML下载器

    主要功能是下载指定的url,这里用到了urllib2

  4.HTML解析器
    主要功能是获取网页上所需的URL和内容,用到BeautifulSoup

    正则表达式的基础知识可以参见

    re模块(正则表达式) – 人生不如戏 – 博客园

    另外安利一个网站,在写正则表达式的时候可以先测试,很实用

    正则表达式在线测试

  5.输出程序

    将爬取到的数据写入HTML文件中,利用HTML的table展示

五、源码

  1.爬虫总调度程序

  1. import url_manager, html_downloader, html_parser, html_outputer
  2. class SpiderMain(object):
  3. def __init__(self):
  4. self.urls = url_manager.UrlManager()
  5. self.downloader = html_downloader.Html_DownloaDer()
  6. self.parser = html_parser.HtmlParser()
  7. self.outputer = html_outputer.HtmlOutputer()
  8. #爬虫调度程序
  9. def craw(self, root_url):
  10. count = 1
  11. #入口URL添加进URL管理器
  12. self.urls.add_new_url(root_url)
  13. #启动循环,获取待扒取的URL,然后交给下载器下载页面,调用解析器解析页面
  14. while self.urls.has_new_url():
  15. try:
  16. new_url = self.urls.get_new_url()
  17. print \'craw\',count, \':\' ,new_url
  18. html_cont = self.downloader.download(new_url)
  19. #得到新的URL列表和内容
  20. new_urls, new_data = self.parser.parse(new_url,html_cont)
  21. #新的URL存到URL管理器,同时进行数据的收集
  22. self.urls.add_new_urls(new_urls)
  23. self.outputer.collect_data(new_data)
  24. if count == 10:
  25. break
  26. count = count +1
  27. except:
  28. print \'craw dailed\'
  29. #调用output_html展示爬取到的数据
  30. self.outputer.output_html()
  31. if __name__ == "__main__":
  32. #入口URL
  33. root_url = "https://item.jd.com/4224129.html"
  34. obj_spider = SpiderMain()
  35. #启动爬虫
  36. obj_spider.craw(root_url)

  2.URL管理器

  1. class UrlManager(object):
  2. def __init__(self):
  3. #未爬取URL列表,已爬取URL列表
  4. self.new_urls = set()
  5. self.old_urls = set()
  6. #判断管理器中是否有新的待扒取的URL
  7. def has_new_url(self):
  8. return len(self.new_urls) != 0
  9. #获取一个新的待扒取的URL
  10. def get_new_url(self):
  11. #pop方法:获取列表中的一个URL并移除它
  12. new_url = self.new_urls.pop()
  13. self.old_urls.add(new_url)
  14. return new_url
  15. #向管理器添加一个新的URL
  16. def add_new_url(self, url):
  17. if url is None:
  18. return
  19. #发现新的未添加的URL,则加入待扒取URL列表
  20. if url not in self.new_urls and url not in self.old_urls:
  21. self.new_urls.add(url)
  22. #向管理器添加批量个新的URL
  23. def add_new_urls(self, urls):
  24. if urls is None or len(urls) == 0:
  25. return
  26. for url in urls:
  27. self.add_new_url(url)

  3.HTML下载器

  1. import urllib2
  2. class Html_DownloaDer():
  3. def download(self, url):
  4. if url is None:
  5. return None
  6. #调用urllib2库的urlopen方法获取 类文件对象(fd) response
  7. """ response = urllib2.urlopen(url)"""
  8.  
  9.  
  10. #调用urllib2库的Request方法创建request对象
  11. request = urllib2.Request(url)
  12. #添加数据
  13. request.add_data(\'a\')
  14. #添加htp和header(伪装成浏览器)
  15. request.add_header(\'User-Agent\',\'Mozilla/5.0\')
  16. #发送请求获取结果
  17. response = urllib2.urlopen(request)
  18. #获取状态码,200表示成功
  19. if response.getcode() != 200:
  20. return None
  21. return response.read()

  4.HTML解析器

  1. from bs4 import BeautifulSoup
  2. import re
  3. import urlparse
  4. class HtmlParser(object):
  5. def _get_new_urls(self, page_url, soup):
  6. new_urls = set()
  7. #获取所有的链接
  8. #格式如:<a target="_blank" title="华为(HUAWEI)..." href="//item.jd.com/12943624333.html">
  9. links = soup.find_all(\'a\',href = re.compile(r"//item.jd.com/\d+\.htm"))
  10. #遍历转化为完整的URL
  11. for link in links:
  12. new_url = link[\'href\']
  13. new_full_url = urlparse.urljoin(page_url,new_url)
  14. #将结果存到一个新的列表里
  15. new_urls.add(new_full_url)
  16. return new_urls
  17. def _new_data(self, page_url, soup):
  18. res_data = {}
  19. #URL
  20. res_data[\'url\'] = page_url
  21. #匹配标题
  22. #<div class="sku-name">华为(HUAWEI) MateBook X 13英寸超轻薄微边框笔记本(i5-7200U 4G 256G 拓展坞 2K屏 指纹 背光 office)灰</div>
  23. title_node = soup.find(\'div\',class_ = "sku-name")
  24. res_data[\'title\'] = title_node.get_text()
  25. #匹配价格
  26. #<div class="dd">
  27. #<span class="p-price"><span>¥</span><span class="price J-p-7430495">4788.00</span></span>
  28. """下载的网页源码无价格信息<span class="price J-p-7430495"></span></span>!!!!!"""
  29. price_node = soup.find(\'span\',class_ = re.compile(r"price\sJ\-p\-\d+"))
  30. res_data[\'price\'] =price_node.get_text()
  31. return res_data
  32. def parse(self, page_url, html_cont):
  33. if page_url is None or html_cont is None:
  34. return
  35. soup = BeautifulSoup(html_cont,\'html.parser\')
  36. new_urls = self._get_new_urls(page_url,soup)
  37. _new_data = self._new_data(page_url,soup)
  38. return new_urls, _new_data

  5.输出程序

  1. class HtmlOutputer(object):
  2. def __init__(self):
  3. self.datas = []
  4. def collect_data(self,data):
  5. if data is None:
  6. return
  7. self.datas.append(data)
  8. def output_html(self):
  9. fout = open(\'output.html\',\'w\')
  10. fout.write("<html>")
  11. fout.write("<head>")
  12. fout.write(\'<meta charset="UTF-8">\')
  13. fout.write("<body>")
  14. fout.write("<table>")
  15. #python默认编码是ascii,中文可能会乱码,故加上encode(\'utf-8\')
  16. for data in self.datas:
  17. fout.write("<tr>")
  18. fout.write("<td>%s</td>" % data[\'url\'])
  19. fout.write("<td>%s</td>" % data[\'title\'].encode(\'utf-8\'))
  20. fout.write("<td>%s</td>" % data[\'price\'].encode(\'utf-8\'))
  21. fout.write("</tr>")
  22. fout.write("</table>")
  23. fout.write("</body>")
  24. fout.write("</head>")
  25. fout.write("</html>")

六、待解决问题

  关于我爬取不到价格的问题…

  调试中发现我已爬取到了对应的内容,唯独少了价格…一度扎心啊…

  

  在 Python爬虫——实战一:爬取京东产品价格(逆向工程方法) – CSDN博客  上看到以下论述

  但是…我查看源码的时候真的是有价格的啊…求大神解惑

 

ps:第一次写博客,思路不是很清晰,欢迎学习交流指正。

版权声明:本文为JYU-hsy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/JYU-hsy/p/9673454.html