随手用python写一个下载源码爬虫试试

Posted 等你归去来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了随手用python写一个下载源码爬虫试试相关的知识,希望对你有一定的参考价值。

  最近在研读jdk源码,网上找了下资源,发现都不完整。

  后来新发现了一个有完整源码的地方,主要包括了java,c,c++的东西,装逼需要,就想拿来玩玩。但是,找了好多种下载打开的方式,发现都不对。于是,我随手写了python爬虫,把他搞定。

1. 思路分析

  1.1. 目标地址:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/dddb1b026323/,打开后先自己看下,是否符合自己的需求;

  1.2. 分析此结构下主要有两种形式,一是目录文件,二是最终文件,特征明显,可区分出最终结果;

  1.3. 目录深度不确定,很自然地想到了递归;

  1.4. 查询有效目录,很自然地想到了正则表达式;

  1.5. 基于可能有中断的情况,可能需要进行断点下载,因此考虑加个简单的跳过功能;

      1.6. 考虑到可能并发下载,为节省时间,应使用锁避免;

  1.7. 考虑可能出现重复下载某文件或目录的情况,耗费资源,因此加一个全局文件集,进行去重处理;

  1.8. 由于该文件目录很规律,就直接沿用其目录结构了;

  1.9. 考虑到本机环境可能不稳定,于是利用公司测试环境服务器;

  2.0. 开工!

2. 鲁棒的代码来一波

  python2 版本的拉取代码如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import urllib,urllib2
import re
import os
import htmlParser
dirbase = \'/tmp\'
urlbase = \'http://hg.openjdk.java.net\'
url= urlbase + \'/jdk8u/jdk8u/jdk/file/dddb1b026323/src\'        #/jdk,/hotspot
skip_to_p = \'\'
skip_find = False;
textmod ={\'user\':\'admin\',\'password\':\'admin\'}
textmod = urllib.urlencode(textmod)
print(url)
req = urllib2.Request(url = \'%s%s%s\' % (url,\'?\',textmod))
res = urllib2.urlopen(req)
res = res.read()
alink = re.findall(r\'<a\',res)
allflist = []

table=re.findall(r\'<tbody class="stripes2">(.+)<\\/tbody>\',res, re.S)

harr = re.findall(r\'href="(/jdk8u[\\w\\/\\._]+)">(?!\\[up\\])\', table[0])

def down_src_recursion(harr):
  global allflist,skip_find;
  if(not harr):
    return False;
  i=0; arrlen = len(harr)
  lock_conflict_jump_max = 2;   # 遇到文件锁时跳过n个文件,当前仍需跳过的文件数量
  lock_conflict_jumping = 0;
  print("in new dir cur...")
  if(len(allflist) > 1500):
     print(\'over 1500, cut to 50 exists...\')
     allflist = allflist[-800:]
  for alink in harr:
    i += 1;
    alink = alink.rstrip(\'/\')
    if(skip_to_p and not skip_find):
    if(alink != skip_to_p):
      print(\'skip file, cause no find..., skip=%s,now=%s\' % (skip_to_p, alink))
      continue;
    else:
      skip_find = True;
    if(alink in allflist):
      print(\'目录已搜寻过:\' + alink)
      continue;
    pa = dirbase + alink
    if(os.path.isfile(pa)):
      print(\'文件已存在,无需下载: \' + pa)
      continue;
    lockfile=pa+\'.tmp\'
    if(os.path.isfile(lockfile)):
    lock_conflict_jumping = lock_conflict_jump_max;
print(\'文件正在下载中,跳过+%s...: %s\' % (lock_conflict_jumping, lockfile))continue; else: if(lock_conflict_jumping > 0):
lock_conflict_jumping -= 1;
print(\'文件正在下载中,跳过+%s...: %s\' % (lock_conflict_jumping, lockfile))continue; # 首先根据后缀把下载中的标识标记好,因为网络下载时间更慢,等下载好后再加标识其实已为时已晚 if(pa.endswith((\'.gif\',\'.jpg\',\'.png\', \'.xml\', \'.cfg\', \'.properties\', \'.make\', \'.sh\', \'.bat\', \'.html\', \'.c\',\'.cpp\', \'.h\', \'.hpp\', \'.java\', \'.1\'))): os.mknod(lockfile); reqt = urllib2.Request(urlbase + alink) rest = urllib2.urlopen(reqt) rest = rest.read() allflist.append(alink) if(rest.find(\'class="sourcefirst"\') > 0): print(\'这是个资源文件:%s %d/%d\' % (alink, i, arrlen)) if(not os.path.isfile(lockfile)): os.mknod(lockfile); filename = alink.split(\'/\')[-1] linearr = re.findall(r\'<span id=".+">(.+)</span>\', rest) fileObject = open(dirbase + alink, \'w\') for line in linearr: try: line = HTMLParser.HTMLParser().unescape(line) except UnicodeDecodeError as e: print(\'oops, ascii convert error accour:\', e) fileObject.write(line + \'\\r\\n\') fileObject.close() os.remove(lockfile); else: print(\'这是目录:%s %d/%d\' % (alink, i, arrlen)) if(not os.path.exists(pa)): print(\'创建目录:%s\' % alink) os.makedirs(\'/tmp\' + alink, mode=0777) ta=re.findall(r\'<tbody class="stripes2">(.+)<\\/tbody>\',rest, re.S) ha = re.findall(r\'href="(/jdk8u[\\w\\/\\._]+)">(?!\\[up\\])\', ta[0]) down_src_recursion(ha) # go... down_src_recursion(harr);

 

  python3则稍微有些写法不一样,python3版本如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

from urllib import parse
from urllib import request
import urllib
import re
import os
import html
dirbase = \'/tmp\'
urlbase = \'http://hg.openjdk.java.net\'
url= urlbase + \'/jdk8u/jdk8u/jdk/file/dddb1b026323/src\'        #/jdk,/hotspot
skip_to_p = \'\'
skip_find = False;
textmod ={\'user\':\'admin\',\'password\':\'admin\'}
textmod = parse.urlencode(textmod)
print(url)
req = request.Request(url = \'%s%s%s\' % (url,\'?\',textmod))
res = request.urlopen(req)
res = res.read().decode("utf-8")
alink = re.findall(r\'<a\',res)
allflist = []

table=re.findall(r\'<tbody class="stripes2">(.+)<\\/tbody>\',res, re.S)

harr = re.findall(r\'href="(/jdk8u[\\w\\/\\._]+)">(?!\\[up\\])\', table[0])

def down_src_recursion(harr):
  global allflist,skip_find;
  if(not harr):
    return False;
  i=0; arrlen = len(harr)
  lock_conflict_jump_max = 2;   # 遇到文件锁时跳过n个文件,当前仍需跳过的文件数量
  lock_conflict_jumping = 0;
  print("in new dir cur...")
  if(len(allflist) > 1500):
     print(\'over 1500, cut to 50 exists...\')
     allflist = allflist[-800:]
  for alink in harr:
    i += 1;
    alink = alink.rstrip(\'/\')
    if(skip_to_p and not skip_find):
      if(alink != skip_to_p):
        print(\'skip file, cause no find..., skip=%s,now=%s\' % (skip_to_p, alink))
        continue;
    else:
      skip_find = True;
    if(alink in allflist):
      print(\'目录已搜寻过:\' + alink)
      continue;
    pa = dirbase + alink
    if(os.path.isfile(pa)):
      print(\'文件已存在,无需下载: \' + pa)
      continue;
    lockfile=pa+\'.tmp\'
    if(os.path.isfile(lockfile)):
      lock_conflict_jumping = lock_conflict_jump_max;
      print(\'文件正在下载中,跳过+%s...: %s\' % (lock_conflict_jumping, lockfile))
      continue; 
    else:
      if(lock_conflict_jumping > 0):         
        lock_conflict_jumping -= 1;
        print(\'文件正在下载中,跳过+%s...: %s\' % (lock_conflict_jumping, lockfile))
        continue;
    # 首先根据后缀把下载中的标识标记好,因为网络下载时间更慢,等下载好后再加标识其实已为时已晚
    if(pa.endswith((\'.gif\',\'.jpg\',\'.png\', \'.xml\', \'.cfg\', \'.properties\', \'.make\', \'.sh\', \'.bat\', \'.html\', \'.c\',\'.cpp\', \'.h\', \'.hpp\', \'.java\', \'.1\'))):
       # os.mknod(lockfile);
       open(lockfile, \'w\')
    reqt = request.Request(urlbase + alink)
    rest = request.urlopen(reqt)
    rest = rest.read().decode("UTF-8")
    allflist.append(alink)
    if(rest.find(\'class="sourcefirst"\') > 0):
       print(\'这是个资源文件:%s         %d/%d\' % (alink, i, arrlen))
       if(not os.path.isfile(lockfile)):
          # os.mknod(lockfile);
          open(lockfile, \'w\')
       filename = alink.split(\'/\')[-1]
       linearr = re.findall(r\'<span id=".+">(.+)</span>\', rest)
       fileObject = open(dirbase + alink, \'w\')
       for line in linearr:
          try:
            line = html.unescape(line)
          except UnicodeDecodeError as e:
            print(\'oops, ascii convert error accour:\', e)
          fileObject.write(line + \'\\r\\n\')
       fileObject.close()
       os.remove(lockfile); 
    else:
      print(\'这是目录:%s        %d/%d\' % (alink, i, arrlen))
      if(not os.path.exists(pa)):
        print(\'创建目录:%s\' % alink)
        os.makedirs(\'/tmp\' + alink, mode=777)
      ta=re.findall(r\'<tbody class="stripes2">(.+)<\\/tbody>\',rest, re.S)
      ha = re.findall(r\'href="(/jdk8u[\\w\\/\\._]+)">(?!\\[up\\])\', ta[0])
      down_src_recursion(ha)

# go...
down_src_recursion(harr);

   python2 与 python3 有许多差别,这是比较头疼的地方。这一点,java就做得很好,几十年以来变化都是兼容的。但这也导致语言维护更困难吧。python3中抛弃了许多python2的接口,包也做了转移。头疼!

3. 让代码跑起来

python jdk-crawler.py

  因为本段代码是单线程的,所以并发度为1. 所以,下载的快慢完全取决于网络io.如果想下载快点,那么可以开几个窗口,调用几次本段python脚本,从而达到并发执行的效果。因为上段代码中,使用了文件锁的方式,所以可以保证多进程之间安全并发。

4. 瞅瞅下载得咋样了

du -sh /tmp/jdk8u/

 

ok, 以上,就打完了。等测试环境下载完成后,再通过ftp搬到你电脑上了。

 

以上是关于随手用python写一个下载源码爬虫试试的主要内容,如果未能解决你的问题,请参考以下文章

python爬虫:爬取百度云盘资料,保存下载地址链接标题链接详情

Python练习册 第 0013 题: 用 Python 写一个爬图片的程序,爬 这个链接里的日本妹子图片 :-),(http://tieba.baidu.com/p/2166231880)(代码片段

《用Python写网络爬虫》示例网站搭建(框架+本书pdf+章节代码)

用python零基础写爬虫--编写第一个网络爬虫

用python零基础写爬虫--编写第一个网络爬虫 -2 设置用户代理

Python爬虫项目实战—全站 950 套美女写真套图爬虫下载