额,早睡还不能早起,周六都要补课、、、
上一篇用BS分析好界面元素之后,将我们需要的信息放到一个info的list里面给返回来出来,方便期间,info包括这些东西
## """ return 12 infos
## 1.title 2.limit des 3.problem des 4.input 5.output
## 6.sample input 7.sample output 8.hint 9.author
## 10.source 11.recommend 12.imgages
## the last element is a list of images """
info的最后一个元素是一个图片的list。
现在那,有了这些个信息,就需要把他们存到数据库里了。额,想了一下,图片的地址就不存来,直接把图片下载下来,然后通过html来引用就好了。所以,需要记录info里面的前11条信息,以及1个题号。
先上代码(需要导入的东西忘了粘了from sqlalchemy import * from sqlalchemy.orm import * ):
1 def store(start, end, url='http://acm.hdu.edu.cn/showproblem.php?pid='):
2 engine = create_engine("mysql://root:duoduo@localhost:3306/test?charset=utf8", encoding="utf-8", echo=True)
3 metadata = MetaData()
4 hdoj_table = Table('hdoj', metadata,
5 Column('problem_id', Integer, primary_key=True),
6 Column('title', String(255), nullable=False),
7 Column('limit_description', String(255), nullable=False),
8 Column('problem_description', Text, nullable=False),
9 Column('input', Text, nullable=False),
10 Column('output', Text, nullable=False),
11 Column('sample_input', Text, nullable=False),
12 Column('sample_output', Text, nullable=False),
13 Column('hint', Text, nullable=True),
14 Column('author', String(40), nullable=True),
15 Column('source', Text, nullable=True),
16 Column('recommend', String(255), nullable=True),
17 )
18
19 class Hdoj(object):
20 def __init__(self, problem_id, title, limit_description, problem_description, input, output, sample_input, sample_output, hint, author, source, recommend):
21 self.problem_id = problem_id
22 self.title = title
23 self.limit_description = limit_description
24 self.problem_description = problem_description
25 self.input = input
26 self.output = output
27 self.sample_input = sample_input
28 self.sample_output = sample_output
29 self.hint = hint
30 self.author = author
31 self.source = source
32 self.recommend = recommend
33
34 def __repr__(self):
35 return "<Hdoj('%s')>" % (self, title)
36
37 metadata.create_all(engine)
38
39 mapper(Hdoj, hdoj_table)
40
41 Session = sessionmaker(autoflush=True, bind=engine)
42
43 session = Session()
44
45 data = []
46 images = []
47 for i in range(start, end):
48 problem_id = str(i)
49 info = catch(url + problem_id)
50 if info[2] == 'None':
51 continue
52 data.append(Hdoj(problem_id, info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7], info[8], info[9], info[10]))
53 images.append(info[-1])
54
55
56 length = len(data)
57 for i in range(length):
58 try:
59 session.add(data[i])
60 session.flush()
61 print 'adding'
62 except Exception as e:
63 #print 'exception'
64 #print e
65 session.rollback()
66 pass
67 return images
解释:
1 def store(start, end, url='http://acm.hdu.edu.cn/showproblem.php?pid='):
start和end是开始到结束的题号,例如我想把1000~2000题都给存下来,那么就store(1000,2000)就好了,url就是之前分析过的默认题目前缀。
2 engine = create_engine("mysql://root:123@localhost:3306/test?charset=utf8", encoding="utf-8", echo=True)
sqlalchemy里面的,sqlalchemy那就像是一个抽象出来的数据库基类一样的东西,你只需要建立一个数据库的引擎,然后就可以在这个数据库上操作,具体的数据库的一些建表插入操作都不用你来操心了,非常方便。例如,我这个引擎是mysql的,用户名为root,密码123,字符集utf8,echo是为了显示一下sqlalchemy到底干了什么,所以设定为True,当然正式发布的时候需要改成False。
1 metadata = MetaData()
2 hdoj_table = Table('hdoj', metadata,
3 Column('problem_id', Integer, primary_key=True),
4 Column('title', String(255), nullable=False),
5 Column('limit_description', String(255), nullable=False),
6 Column('problem_description', Text, nullable=False),
7 Column('input', Text, nullable=False),
8 Column('output', Text, nullable=False),
9 Column('sample_input', Text, nullable=False),
10 Column('sample_output', Text, nullable=False),
11 Column('hint', Text, nullable=True),
12 Column('author', String(40), nullable=True),
13 Column('source', Text, nullable=True),
14 Column('recommend', String(255), nullable=True),
15 )
metadata是神马我不是很了解,(元数据??),总之先记下来用着,然后就是新建一个表,每个oj一张表,所以,就有了上面这2~15行,单词描述的比较明显,不解释了就。
1 class Hdoj(object):
2 def __init__(self, problem_id, title, limit_description, problem_description, input, output, sample_input, sample_output, hint, author, source, recommend):
3 self.problem_id = problem_id
4 self.title = title
5 self.limit_description = limit_description
6 self.problem_description = problem_description
7 self.input = input
8 self.output = output
9 self.sample_input = sample_input
10 self.sample_output = sample_output
11 self.hint = hint
12 self.author = author
13 self.source = source
14 self.recommend = recommend
15
16 def __repr__(self):
17 return "<Hdoj('%s')>" % (self, title)
18
19 metadata.create_all(engine)
20
21 mapper(Hdoj, hdoj_table)
接下来新建一个Hdoj的类,并且讲这个类与刚刚建的数据表做好映射。
1~14行就是Hdoj了
19、20行里面,是sqlalchemy的东西了,19行是真正的建立引擎,20行是将类与的数据库的表做映射。
接下来是存储:
1 Session = sessionmaker(autoflush=True, bind=engine)
2
3 session = Session()
4
5 data = []
6 images = []
7 for i in range(start, end):
8 problem_id = str(i)
9 info = catch(url + problem_id)
10 if info[2] == 'None':
11 continue
12 data.append(Hdoj(problem_id, info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7], info[8], info[9], info[10]))
13 images.append(info[-1])
14
15
16 length = len(data)
17 for i in range(length):
18 try:
19 session.add(data[i])
20 session.flush()
21 print 'adding'
22 except Exception as e:
23 #print 'exception'
24 #print e
25 session.rollback()
26 pass
27 return images
session这个东西,个人的理解就是一次链接,在这儿一个session就是python和数据库的一次链接,然后我们对这个session进行一系列操作,存储啊添加啊神马的,注意,只要session没有flush(),操作都是在缓存里面进行的,查文档发现commit()是提交的一个函数,它里面是会调用flush()的。
1 Session = sessionmaker(autoflush=True, bind=engine)
用sessionmaker建立一个Session,绑定了之前建立的引擎。
3 session = Session()
建立一个Session(你用sessionmaker建立的特殊的一个链接)的对象。
5~13都是添加对象
8 problem_id = str(i)
9 info = catch(url + problem_id)
用之前写好的catch函数抓取需要的信息,存到info里面,注意url变了,这样来进行题目的遍历。
10 if info[2] == 'None':
11 continue
这两行是防止神马的,防止数据库加入没有的题目,比如杭电的题目就到4186,如果store(10000,10010),会把着些没有的url给存到数据库里面,这不是我们想要的,如果没有这个题目的话,让他默默的失败,进行下一个链接。
5 data = []
6 images = []
12 data.append(Hdoj(problem_id, info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7], info[8], info[9], info[10]))
13 images.append(info[-1])
data是我真正想存到数据库里面的信息,images则是不想存到里面的图片信息,分开分开。
1 length = len(data)
2 for i in range(length):
3 try:
4 session.add(data[i])
5 session.flush()
6 print 'adding'
7 except Exception as e:
8 #print 'exception'
9 #print e
10 session.rollback()
11 pass
12 return images
这几行是最重要,这是真正把数据放到数据库里的。
首先要说明为什马要用异常,我的主键用的是题号啊,第一次store(1000,1120)的时候一般是不会错的,如果第二次我想store(1100,1200),数据库会告诉我主键重复,不能添加,额,这个当时纠结了好久,各种查,无果,学长告诉我说你可以抓异常啊,弱弱的说一句,我这还真是第一次用异常,没想到介么好用、、
然后是rollback(),rollback像他的名字一样,回滚。session在进行一次链接的时候,每条数据和数据库链接在一起,直到等到rollback它才进行下一条数据的链接(这都是我自己感觉的,如果不对啊话@我下下,学习中),额、怎么说那,举个例子吧,如果说我存store(1055,1070),在1056题,4行添加数据,5行想要更新缓存,写入数据库的时候出现异常了,假设没有rollback这个函数,仅仅是pass的话,session着个叫做链接的东西依然把python和数据库里面的1056进行链接,而不会向下走,结果就是下面的14条数据继续异常,而且给的异常都是第1056题目的主键重复,就是说在循环继续后,数据库并没有断开和1056题的链接,依然尝试把1056题的数据加进来。(这些是真的啊,就不回去实验截图了,一会儿还要上课)
信息要一条一条的存,包不准那一条就会有异常。虽然说session有个addAll()函数,但是如果里面有一条数据出了异常,如果用我第10行的处理机制,那么那一条异常数据后面的所有数据全部都不能添加了。
由于不想把图片存到数据库里面,把它直接return出去了,新写一个函数把图片存到本地。
就是用python处理文件的那一套,代码:
1 def download_img(url, default_url_head='http://acm.hdu.edu.cn/data/images/', default_file_head='/home/duoduo/images/', pro_file_head='hdoj'):
2 filename = url.split('/')[-1]
3 url_head = default_url_head
4 file_head = default_file_head
5
6 url_path = url_head + filename
7 #print url_path
8 file_path = file_head + pro_file_head + '/' + filename
9 #print file_path
10 try:
11 open_img = urllib2.urlopen(url_path)
12 img = open_img.read()
13 img_file = open(file_path, 'wb')
14 img_file.write(img)
15 img_file.close()
16 print 'downloading images from:' + url_path
17 except:
18 print 'HTTP Error 404: Not Found'
19 pass
第2~9行就是把url和本地的路径进行一些处理
10~19行下载,依然要抓异常,因为有些图本身就挂了,打不开,会弹出404异常
13~15就是文件的读写操作,把图片信息存起来来。
存储就到这里了,然后包一下,做一个start函数,把上面的东西都给装进来:
1 def start(begin, end):
2 if(end < begin):
3 print '输入有误,请检查您的输入。'
4 return None
5
6 default_group_num = 50
7 groups = (end - begin) / default_group_num
8
9 for i in range(groups):
10 group_start = i * default_group_num + begin
11 group_end = (i + 1) * default_group_num + begin
12 images_list = store(group_start, group_end)
13 len_img_list = len(images_list)
14 for i in range(len_img_list):
15 len_img = len(images_list[i])
16 for j in range(len_img):
17 download_img(images_list[i][j])
18
19 if (end - begin) >= 0 :
20 last_group_start = groups * default_group_num + begin
21 images_list = store(last_group_start, end + 1)
22 len_img_list = len(images_list)
23 for i in range(len_img_list):
24 len_img = len(images_list[i])
25 for j in range(len_img):
26 download_img(images_list[i][j])
6 default_group_num = 50
默认一次存多少数据,我怕一次存太多,内存受不了,或者是等了10来分钟结果中途崩了,还要从头来,麻烦。
7 groups = (end - begin) / default_group_num
groups以及一下的for循环就是做分组处理的。
python抓数据,存数据就彻底ok了,下一步就是用django显示看看是否正确,django的静态文件这次是深刻体会了,上课去先,晚上或者明天在写下一篇吧。