python3爬虫学习

来源:互联网 发布:淘宝怎么付钱 编辑:程序博客网 时间:2024/05/22 00:54

Python3爬虫学习--多线程爬取图片

python3爬虫


本人是个python爬虫小白,也没有任何编程经验,在一个偶然的机会浏览某论坛发现了大量的妹子图,于是想着能不能写个简单的爬虫将这些图片爬下来再看,于是在网上找各种资料发现python实现爬虫比较简单、入门也低,于是自己慢慢摸索写了个论坛妹子图片的多线程爬虫,实现将特定主题帖子中的妹子图爬取后保存在本地PC中。

python3环境准备

  • 先到www.python.org官网下载python安装包,建议新手学习使用python3吧,本人使用的是python-3.5.4-amd64安装包,安装完成后就可以使用python shell环境写代码了,但是这种环境不适合软件工程开发。

  • 建议使用sublime text或者pycharm集成开发环境,sublime text适合轻量级软件工程开发,而pycharm适合大型软件工程开发,具体安装配置使用请自行百度下。

python3第三方库安装

  • python3本身也内置了urllib网页请求模块,但是我比较喜欢用requests这个库,因为简单易懂。

    • 在windows cmd命令窗口中安装requests库:pip3 install requests

  • 还有个就是网页解析库,网上多数人建议使用bs4库,但是我发现lxml库的效率比bs4快些,我个人比较倾向于使用lxml库。

    • 在windows cmd命令窗口中安装lxml库:pip3 install lxml

最后用pip3 list确认下是否安装成功,如果cmd窗口中显示如下则表示安装成功了。

爬虫准备工作--分析网站结构

国内著名的MM图片论坛网站,url就不放出来了(容易被和谐,但我的GitHub源码中有),一般的论坛网站都是首页->论坛主题->帖子主题列表->帖子主题内容,这样的页面结构。可以根据这种页面结构来组织编写代码,代码实现逻辑如下图所示:

爬虫代码实现说明

  • get_html代码说明

    def get_html(url):      try:          # 使用requests库中的get方法请求页面          r = s.get(url=url,headers=header,timeout=12)          if r.status_code == 404:              print(' %s 响应状态码非200 OK,正在重试中......' % url)              time.sleep(6)              r = s.get(url=url,headers=header,timeout=15)          global num      except Exception as err:          print(err,'\n请求 %s 失败,正在重试中......' % url)          time.sleep(3)          while True:              try:                  r = s.get(url=url,headers=header,timeout=15)                  if r.status_code == 404:                      print(' %s 响应状态码非200 OK,正在重试中......' % url)                      time.sleep(6)                      r = s.get(url=url,headers=header,timeout=15)              except Exception as err:                  print(err,'\n请求重试 %s 继续失败,继续重试中......' % url)                  time.sleep(3)              else:                  r.encoding = 'GB18030'                  if r.text.find('404 Not Found') != -1:                      print(' %s 重试失败!放弃访问!' % url,r)                      return r.text                  else:                      print('请求重试 %s 成功!' % ('【'+str(num)+'】'+ url),r)                      num += 1                      return r.text      else:          r.encoding = 'GB18030'          if r.text.find('404 Not Found') != -1:              print(' %s 重试失败!放弃访问!' % url,r)              return r.text          else:              print(' %s 访问成功!' % ('【'+str(num)+'】'+ url),r)              num += 1              return r.text

其中最外层try中的代码块实现页面请求以及如果响应状态码非200ok时会重新请求一次;最外层的except中代码块捕捉到任何请求异常就会重新发起页面请求直至成功;最外层else中代码块作用为当try语句正常执行后就会执行else中的语句。

  • list_page代码说明

    def list_page(url):      page = get_html(url)      # 使用lxml库的html模块解析页面      tree = html.fromstring(page)      # 先使用lxml的xpath方法获取所有帖子主题列表的标签,然后利用正则表达式过滤所需要的所有帖子主题列表url      pages_url = re.findall(r'htm_data/16/17\d+/\d{7}\.html',str(tree.xpath('//h3/a/@href')))      for i in range(len(pages_url)):      # 拼接真正的帖子主题列表url地址          pages_url[i] = 'http://cl.***.xyz/'+pages_url[i]      return pages_url

此函数的功能是获取每页中所有主题列表的url地址。

  • get_page_html代码说明

  def get_page_html(url):      pages = list_page(url)          for page in pages:              page = get_html(page)              tree = html.fromstring(page)              # 使用lxml的xpath方法获取主题内容的标题              title_name = tree.xpath('//h4/text()')              if len(title_name) != 0:                  # 替换不符合windows系统文件命名的特殊符号                  file_name = title_name[0].replace(':','_')\                                          .replace('?','_')\                                          .replace('<','(')\                                          .replace('>',')')\                                          .replace('"',' ')\                                          .replace('*','_')\                                          .replace('\u301c','')\                                          .replace('\xa0','_')              else:                  file_name = None                  continue             # 使用lxml的xpath方法获取主题内容中图片的url地址               img_url_list = tree.xpath('//input/@src')              # 判断主题文件夹是否存在,如果没有则直接创建              if not os.path.exists('D:\\Download\\Pictrue\\'+file_name):                  try:                      os.makedirs('D:\\Download\\Pictrue\\' + file_name)                      os.chdir('D:\\Download\\Pictrue\\' + file_name)                      print('D:\Download\Pictrue\%s 主题目录创建成功!' % file_name)                  except Exception as err:                      print('主题目录创建失败!','\n'+err)                      continue              else:                  print('D:\Download\Pictrue\%s 主题目录已存在!' % file_name)              for img_url in img_url_list:                  # 图片文件名和替换不符合系统文件命名规则的特殊符号                  img_name = img_url.split('/')[-1].translate(str.maketrans('<>?=&','____.'))                 # 判断图片是否下载过,如果没有就直接下载                   if not os.path.isfile('D:\\Download\\Pictrue\\' + file_name+ '\\' + img_name):                     # 调用多线程下载图片                       thread = download_thread(file_name,img_url,img_name)                      thread.start()                      time.sleep(1.5)                  else:                      print(img_name+' >>> 该图片已经下载过了!')              print('该主题共有 %s 张图片!' % len(img_url_list))              time.sleep(5) 

此函数的功能是获取每个帖子主题内容中的标题及图片的url地址并调用多线程下载图片

  • save_img代码说明

    def save_img(file_name,img_url,img_name):      with open('D:\\Download\\Pictrue\\' + file_name + '\\' + img_name,'wb') as file:          print('正在使用线程 %s 下载:' % threading.current_thread().name + file_name +' >>> '+ img_name)          try:              file.write(s.get(img_url,headers=header,timeout=15).content)              print('线程 %s 下载完成!' % threading.current_thread().name)          except Exception as err:              print(err,'\n'+img_url+' >>> URL请求失败,正在重新下载......')              time.sleep(3)              # 下载失败会重试一次              try:                  file.write(s.get(img_url,headers=header,timeout=18).content)                  print('线程 %s 重新下载完成!' % threading.current_thread().name)              except Exception as err:                  print(err,'\n'+img_url+' >>> URL请求继续失败,放弃下载......')

此函数功能是执行图片下载并保存在本地目录。

  • 入口函数的代码说明

  if __name__ == '__main__':      ''' 1.爬取过于频繁会导致ip访问受限制,之后访问需要输入验证码          2.根据不同网络环境判断访问是否需要输入验证码          3.验证码保存在代码所在目录,并需要手动输入      '''      global r      r = s.get('http://cl.***.xyz/index.php',headers=header,timeout=12)      r.encoding = 'GB18030'      #print(r,'\n'+r.text)      if r.text.find('url=codeform.php') != -1:          with open('code.png', 'wb') as file:              print('正在下载验证码图片......\n请到 %s 目录找到code.png并手动输入!' % os.path.abspath('code.png'))              file.write(s.get('http://cl.***.xyz/require/codeimg.php',headers=header,timeout=15).content)              file.close()          code = input('请输入验证码:')          postdata['validate'] = code          r = s.post('http://cl.***.xyz/codeform.php',headers=header,data=postdata)          r.encoding = 'GB18030'          while r.text.find('输入有误') != -1:              print('验证码输入有误!')              with open('code.png', 'wb') as file:                  file.write(s.get('http://cl.***.xyz/require/codeimg.php',headers=header,timeout=15).content)                  file.close()              code = input('请重新输入验证码:')              postdata['validate'] = code              r = s.post('http://cl.***.xyz/codeform.php',headers=header,data=postdata)              r.encoding = 'GB18030'          for index in range(100):              index += 1              url = 'http://cl.***.xyz/thread0806.php?fid=16&search=&page='+str(index)              get_page_html(url)              time.sleep(3)      else:          for index in range(100):              index += 1              url = 'http://cl.***.xyz/thread0806.php?fid=16&search=&page='+str(index)              get_page_html(url)              time.sleep(3)

代码入口函数实现验证码登入(实际上如果爬取不是很频繁是不需要输入验证码的)及爬取前100页的图片资源。

完整GitHub源码链接

由于刚学习python,很多技巧还没有掌握,文章中的代码还有很多处是可以优化的,比如第一个函数和最后一个函数重复的代码比较多,希望高手能够指点下如何优化,大家互相学习嘛!

原创粉丝点击