Youmai の Blog


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

每天一个python小程序(2)--生成激活码

发表于 2015-02-02 | 分类于 python

作为 Apple Store App 独立开发者,你要搞限时促销,为你的应用生成激活码(或者优惠券),使用 Python 如何生成 200 个激活码(或者优惠券)?

本题主要是熟悉python的uuid模块,uuid生成有4个算法,这里采用的是uuid4

#!/usr/bin/python
#encoding=utf-8
import uuid

def generate_activation_code(count):
    code_list = []
    for i in xrange(count):
        code = str(uuid.uuid4()).replace('-','').upper()
        #uuid4的算法有一定的重复概率,所以下面作了判断
        if not code in code_list:
            code_list.append(code)

    return code_list

if __name__ == '__main__':
    code_list = generate_activation_code(200)
    for code in code_list:
        print code

代码可以在这里下载

每天一个python小程序(1)-修改QQ头像

发表于 2015-02-02 | 分类于 python

将你的 QQ 头像(或者微博头像)右上角加上红色的数字,类似于微信未读信息数量那种提示效果。 类似于图中效果

头像

本题用到的知识点主要是python的PIL模块,PIL是python中最常用的图像处理库

#!/usr/bin/python
#encoding=utf-8

from PIL import Image, ImageDraw, ImageFont

def write_num_on_img(filePath, num):
    img = Image.open(filePath)
    #img.size返回一个元组(width, height)
    size = img.size
    draw = ImageDraw.Draw(img)
    #colour设置数字的颜色,这里是红色
    colour = (255, 0, 0)
    ttfont = ImageFont.truetype('ahronbd.ttf', 50)
    #关键函数,参数包括,文字的位置,内容,颜色,字体
    draw.text((size[0]-30, 0), str(num), fill=colour, font=ttfont)
    #显示一幅已载入的图像
    img.show()
    img.save('changed.jpg')

if __name__ == '__main__':
    write_num_on_img("test.jpg", 4)

代码可以在这里下载

python试题

发表于 2015-02-01 | 分类于 python

##list与tuple的区别 ?dictionary与set的区别?
对于list与tupel的区别,在《Python核心编程2》中讲解的很清楚,可以总体概括为,可变与不可变、他们本质相同,但是就因为可变性就延伸了很多不同点。如list类型可以进行相应更改和方法操作,但tupel就没有这个功能。

对于dict和set,一个是Python唯一的映射类型,一个是Python集合。python中集合对象(set)是一组无序排列的可哈希的值,包含两种类型:可变集合(set)和不可变集合(frozenset),所以set不是可哈希的,frozenset是可哈希的,能当作字典的键。字典和集合的详细区别可以点这里

##python中String类型和unicode什么关系?
string是字节串,而unicode是一个统一的字符集,utf-8是它的一种存储实现形式,string可为utf-8编码,也可编码为GBK等各种编码格式

##输出从1加到100的和
sum = 0
for i in range(1, 101):
sum += i
print sum

#注意点:range的右范围是取不到的

##如何在一个函数内修改一个全局变量?
在函数中使用global关键字声明全局变量,然后修改。如:

a = 10

def add1():
    global a
    print a + 1

add1()

##except的用法和作用
Python的except用来捕获所有异常, 因为Python里面的每次错误都会抛出 一个异常,所以每个程序的错误都被当作一个运行时错误。

以下是使用except的一个例子:

try:    
    foo = opne(“file”) #open被错写为opne    
except: 
    sys.exit(“could not open file!”)

因为这个错误是由于open被拼写成opne而造成的,然后被except捕获,所以debug程序的时候很容易不知道出了什么问题

下面这个例子更好点:

try:
    foo = opne(“file”) # 这时候except只捕获IOError
except IOError:
    sys.exit(“could not open file”)

##如何反序的迭代一个序列?how do I iterate over a sequence in reverse order

如果是一个list, 最快的解决方案是:

list.reverse()
try:
    for x in list:
        “do something with x”
finally:
list.reverse()

如果不是list, 最通用但是稍慢的解决方案是:

#range函数的参数
#第一个:从这个数开始迭代
#第二个:迭代到这个数
#第三个:步长,为负表示倒序
for i in range(len(sequence)-1, -1, -1):
    x = sequence[i]

##判断当前用户是否是root
import os
if os.geteuid() != 0:
print “This program must be run as root. Aborting.”
sys.exit(1)

##什么是lambda函数?
Python允许你定义一种单行的小函数。定义lambda函数的形式如下:labmda 参数:表达式lambda函数默认返回表达式的值。你也可以将其赋值给一个变量。lambda函数可以接受任意个参数,包括可选参数,但是表达式只有一个:

>>> g = lambda x, y: x*y
>>> g(3,4)
12
>>> g = lambda x, y=0, z=0: x+y+z
>>> g(1)
1
>>> g(3, 4, 7)
14

也能够直接使用lambda函数,不把它赋值给变量:

>>> (lambda x,y=0,z=0:x+y+z)(3,5,6)
14

如果你的函数非常简单,只有一个表达式,不包含命令,可以考虑lambda函数。否则,你还是定义函数才对,毕竟函数没有这么多限制。

##Python是如何进行内存管理的?
Python的内存管理是由Python得解释器负责的,开发人员可以从内存管理事务中解放出来,致力于应用程序的开发,这样就使得开发的程序错误更少,程序更健壮,开发周期更短

##如何用Python来发送邮件?
以下代码可以在支持SMTP监听器的服务器上执行。

import sys, smtplib

fromaddr = raw_input(“From: “)
toaddrs = raw_input(“To: “).split(‘,’)
print “Enter message, end with ^D:”
msg = ""
while 1:
    line = sys.stdin.readline()
    if not line:
        break
    msg = msg + line

# 发送邮件部分
server = smtplib.SMTP(‘localhost’)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

##Python输出一个Fibonacci数列?

方法一:

a,b = 0, 1
while b<100:
    print (b),
    a, b = b, a+b

方法二:递归

def fibonacci2(n):  
    if n == 1 or n == 2:  
        return 1  
    else:  
        return fibonacci2(n-1) + fibonacci2(n-2)

方法三:迭代

def fibonacci(n):  
    if n == 1 or n == 2:  
        return 1  

    nPre = 1  
    nLast = 1  
    nResult = 0  
    i = 2  
    while i < n:  
        nResult = nPre + nLast  
        nPre = nLast  
        nLast = nResult  
        i += 1  

    return nResult  

##Python里面如何生成随机数?
标准库random实现了一个随机数生成器,实例代码如下:

import random

random.random()

它会返回一个随机的0和1之间的浮点数

python的socket编程

发表于 2015-01-31 | 分类于 python

python的网络编程比c语言简单许多, 封装许多底层的实现细节, 方便程序员使用的同时, 也使程序员比较难了解一些底层的东西, 我觉得学网络编程还是用c语言更好一点。

这篇文章将一些Linux网络编程的函数和Python网络编程函数做了一个简单的对照, 方便记忆。

##Socket套接字的概念
Socket,是操作系统内核中的一个数据结构,它是网络中的节点进行相互通信的门户。它是网络进程的ID。网络通信,归根到底还是进程间的通信(不同计算机上的进程间通信, 又称进程间通信, IP协议进行的主要是端到端通信)。在网络中,每一个节点(计算机或路由)都有一个网络地址,也就是IP地址。两个进程通信时,首先要确定各自所在的网络节点的网络地址。但是,网络地址只能确定进程所在的计算机,而一台计算机上很可能同时运行着多个进程,所以仅凭网络地址还不能确定到底是和网络中的哪一个进程进行通信,因此套接口中还需要包括其他的信息,也就是端口号(PORT)。在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系。

所以,使用端口号和网络地址的组合可以唯一的确定整个网络中的一个网络进程.
端口号的范围从0~65535,一类是由互联网指派名字和号码公司ICANN负责分配给一些常用的应用程序固定使用的“周知的端口”,其值一般为0~1023, 用户自定义端口号一般大于等于1024。

每一个socket都用一个半相关描述{协议、本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示。socket也有一个类似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过socket来实现的

###Socket类型
socket类型在Liunx和Python是一样的, 只是Python中的类型都定义在socket模块中, 调用方式socket.SOCK_XXXX

  • 流式socket(SOCK_STREAM) 用于TCP通信

流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性

  • 数据报socket(SOCK_DGRAM) 用于UDP通信

数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP

  • 原始socket(SOCK_RAW) 用于新的网络协议实现的测试等

原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以, 其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

##Socket编程

###TCP通信
TCP通信的基本步骤如下:

服务端:socket—-bind—-listen—-while(True){—-accept—-recv—-send——}—-close

客户端:socket———————————————connect—-send—-recv———-close

TCP通信.png

####socket函数
使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字

#Linux
int socket(int domain, int type, int protocol);
domain:AF_INET:Ipv4网络协议 AF_INET6:IPv6网络协议
type : tcp:SOCK_STREAM   udp:SOCK_DGRAM
protocol : 指定socket所使用的传输协议编号。通常为0.
返回值:成功则返回套接口描述符,失败返回-1。


#python
socket.socket([family[, type[, proto]]])
family : AF_INET (默认ipv4), AF_INET6(ipv6) or AF_UNIX(Unix系统进程间通信). 
type : SOCK_STREAM (TCP), SOCK_DGRAM(UDP) . 
protocol : 一般为0或者默认
如果socket创建失败会抛出一个socket.error异常

####服务器端函数

#####bind函数
将套接字绑定到地址, python下,以元组(host,port)的形式表示地址, Linux下使用sockaddr_in结构体指针

#Linux
int bind(int sockfd, struct sockaddr * my_addr, int addrlen);
sockfd : 前面socket()的返回值
my_addr : 结构体指针变量
#####
struct sockaddr_in  //常用的结构体
{
unsigned short int sin_family;  //即为sa_family AF_INET
uint16_t sin_port;  //为使用的port编号
struct in_addr sin_addr;  //为IP地址
unsigned char sin_zero[8];  //未使用
};
struct in_addr
{
uint32_t s_addr;
};
####
addrlen : sockaddr的结构体长度。通常是计算sizeof(struct sockaddr);
返回值:成功则返回0,失败返回-1


#python
s.bind(address)
s为socket.socket()返回的套接字对象
address为元组(host,port)
host: ip地址, 为一个字符串
post: 自定义主机号, 为整型

#####listen函数
使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接

#Linux
int listen(int sockfd,int backlog);
sockfd : 为前面socket的返回值.
backlog : 指定同时能处理的最大连接要求,通常为10或者5。最大值可设至128
返回值:成功则返回0,失败返回-1


#python
s.listen(backlog)
s为socket.socket()返回的套接字对象
backlog : 操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了

#####accept函数
接受远程计算机的连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。

#Linux
int accept(int s,struct sockaddr * addr,int * addrlen);
sockfd : 为前面socket的返回值.
addr : 为结构体指针变量,和bind的结构体是同种类型的,系统会把远程主机的信息(远程主机的地址和端口号信息)保存到这个指针所指的结构体中。
addrlen : 表示结构体的长度,为整型指针 
返回值:成功则返回新的socket处理代码new_fd,失败返回-1
#python
s.accept()
s为socket.socket()返回的套接字对象
返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址

####客户端函数

#####connect函数
用来请求连接远程服务器

#Linux
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
sockfd : 为前面socket的返回值.
serv_addr : 为结构体指针变量,存储着远程服务器的IP与端口号信息
addrlen : 表示结构体变量的长度
返回值:成功则返回0,失败返回-1


#python
s.connect(address)
s为socket.socket()返回的套接字对象
address : 格式为元组(hostname,port),如果连接出错,返回socket.error错误

####通用函数
接收远端主机传来的数据

#####recv函数

#Linux
int recv(int sockfd,void *buf,int len,unsigned int flags);
sockfd : 为前面accept的返回值.也就是新的套接字。
buf : 表示缓冲区
len : 表示缓冲区的长度
flags : 通常为0
返回值:成功则返回实际接收到的字符数,可能会少于你所指定的接收长度。失败返回-1


#python
s.recv(bufsize[,flag])
s为socket.socket()返回的套接字对象
bufsize : 指定要接收的数据大小
flag : 提供有关消息的其他信息,通常可以忽略
返回值为数据以字符串形式

#####send函数
发送数据给指定的远端主机

#Linux
int send(int s,const void * msg,int len,unsigned int flags);
sockfd : 为前面socket的返回值.
msg : 一般为常量字符串
len : 表示长度
flags : 通常为0
返回值:成功则返回实际传送出去的字符数,可能会少于你所指定的发送长度。失败返回-1


#python
s.send(string[,flag])
s为socket.socket()返回的套接字对象
string : 要发送的字符串数据 
flag : 提供有关消息的其他信息,通常可以忽略
返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall(string[,flag])
#完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。
返回值 : 成功返回None,失败则抛出异常。

#####close函数
关闭套接字

#Linux
int close(int fd);
fd : 为前面的sockfd
返回值:若文件顺利关闭则返回0,发生错误时返回-1


#python
s.close()
s为socket.socket()返回的套接字对象

####简单的客户端服务器TCP连接
一个简单的回显服务器和客户端模型, 客户端发出的数据, 服务器会回显到客户端的终端上(只是一个简单的模型, 没考虑错误处理等问题)

#服务器端
#!/usr/bin/python
# -*- coding:utf-8 -*-
import socket   #socket模块
import commands   #执行系统命令模块
BUF_SIZE = 1024  #设置缓冲区大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #生成一个新的socket对象
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  #设置地址复用
server.bind(server_addr)  #绑定地址
server.listen(5)  #监听, 最大监听数为5
while True:
    client, client_addr = server.accept()  #接收TCP连接, 并返回新的套接字和地址
    print 'Connected by', client_addr
    while True :
        data = client.recv(BUF_SIZE)  #从客户端接收数据
        print data
        client.sendall(data)  #发送数据到客户端
server.close()



#客户端
#!/usr/bin/python
# -*- coding:utf-8 -*-
import socket
BUF_SIZE = 1024  #设置缓冲区的大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #返回新的socket对象
client.connect(server_addr)  #要连接的服务器地址
while True:
    data = raw_input("Please input some string > ")  
    client.sendall(data)  #发送数据到服务器
    data = client.recv(BUF_SIZE)  #从服务器端接收数据
    print data
client.close()

####带错误处理的客户端服务器TCP连接
在进行网络编程时, 最好使用大量的错误处理, 能够尽量的发现错误, 也能够使代码显得更加严谨

#服务器端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import socket   #socket模块
BUF_SIZE = 1024  #设置缓冲区大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
try :
  server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #生成一个新的socket对象
except socket.error, msg :
    print "Creating Socket Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1]
    sys.exit()
print "Socket Created!"
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  #设置地址复用
try : 
    server.bind(server_addr)  #绑定地址
except socket.error, msg :
  print "Binding Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1]
  sys.exit()
print "Socket Bind!"
server.listen(5)  #监听, 最大监听数为5
print "Socket listening"
while True:
    client, client_addr = server.accept()  #接收TCP连接, 并返回新的套接字和地址, 阻塞函数
    print 'Connected by', client_addr
    while True :
        data = client.recv(BUF_SIZE)  #从客户端接收数据
        print data
        client.sendall(data)  #发送数据到客户端
server.close()


#客户端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import socket
BUF_SIZE = 1024  #设置缓冲区的大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
try : 
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #返回新的socket对象
except socket.error, msg :
    print "Creating Socket Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1]
    sys.exit()
client.connect(server_addr)  #要连接的服务器地址
while True:
    data = raw_input("Please input some string > ")  
    if not data :
        print "input can't empty, Please input again.."
        continue
    client.sendall(data)  #发送数据到服务器
    data = client.recv(BUF_SIZE)  #从服务器端接收数据
    print data
client.close()

###UDP通信
UDP通信流程图如下:

服务端:socket—-bind—-recvfrom—-sendto—-close

客户端:socket—————sendto—-recvfrom—-close

UDP通信

#####sendto()函数
发送UDP数据, 将数据发送到套接字

#Linux
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
sockfd : 为前面socket的返回值.
msg : 一般为常量字符串
len : 表示长度
flags : 通常为0
to : 表示目地机的IP地址和端口号信息, 表示地址的结构体
tolen : 常常被赋值为sizeof (struct sockaddr)
返回值 : 返回实际发送的数据字节长度或在出现发送错误时返回-1。


#Python
s.sendto(string[,flag],address)
s为socket.socket()返回的套接字对象
address : 指定远程地址, 形式为(ipaddr,port)的元组
flag : 提供有关消息的其他信息,通常可以忽略
返回值 : 发送的字节数。

#####recvfrom()函数
接受UDP套接字的数据, 与recv()类似

#Linux
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
sockfd : 为前面socket的返回值.
msg : 一般为常量字符串
len : 表示长度
flags : 通常为0
from :是一个struct sockaddr类型的变量,该变量保存连接机的IP地址及端口号
fromlen : 常置为sizeof (struct sockaddr)。
返回值 : 返回接收到的字节数或当出现错误时返回-1,并置相应的errno。


#Python
s.recvfrom(bufsize[.flag])
返回值 : (data,address)元组, 其中data是包含接收数据的字符串,address是发送数据的套接字地址
bufsize : 指定要接收的数据大小
flag : 提供有关消息的其他信息,通常可以忽略

####2.4. 简单的客户端服务器UDP连接

#服务器端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
BUF_SIZE = 1024  #设置缓冲区大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  #生成新的套接字对象
server.bind(server_addr)  #套接字绑定IP和端口
while True :
    print "waitting for data"
    data, client_addr = server.recvfrom(BUF_SIZE)  #从客户端接收数据
    print 'Connected by', client_addr, ' Receive Data : ', data
    server.sendto(data, client_addr)  #发送数据给客户端
server.close()


#客户端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct
BUF_SIZE = 1024  #设置缓冲区
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  #生成新的套接字对象
while True :
    data = raw_input('Please Input data > ')
    client.sendto(data, server_addr)  #向服务器发送数据
    data, addr = client.recvfrom(BUF_SIZE)  #从服务器接收数据
    print "Data : ", data
client.close()

###2.5. 其他

s.getpeername()
#返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname()
#返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value)
#设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen])
#返回套接字选项的值。
s.settimeout(timeout)
#设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout()
#返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno()
#返回套接字的文件描述符。
s.setblocking(flag)
#如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile()
#创建一个与该套接字相关连的文件

##3. 参考链接
python-socket官方文档

如何重命名scrapy下载的图片

发表于 2015-01-30 | 分类于 scrapy

默认情况下,使用ImagePipeline组件下载图片的时候,图片名称是以图片URL的SHA1值进行保存的。

如:
图片URL:http://www.example.com/image.jpg

SHA1结果:3afec3b4765f8f0a07b78f98c07b83f013567a0a

则图片名称:3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg

但是,我想要以原来的图片名称进行保存,比如上面例子中的图片保存到本地的话,图片名称就应该是:image.jpg

stackoverflow上说是可以重写image_key函数,不过我试了下,结果发现不行,重写的image_key函数没被调用。其实不是stackoverflow上的回答是错误的,在scrapy 0.12之前,确实应该这样来解决此问题,现在scrapy的版本已经更新了很多,这个方法不适用了,下面是ImagePipeline的源码中的提醒:

ImagesPipeline.image_key(url) and file_key(url) methods are deprecated, please use file_path(request, response=None, info=None) instead

也就是说,在最新版本的Scrapy中(0.24.4),使用file_path代替image_key函数。

下面是新版本的解决方法,在scrapy 0.24.4中测试通过

  • 在pipelines.py中自定义ImagePipeline类,并重写file_path函数

    from scrapy.contrib.pipeline.images import ImagesPipeline
    from scrapy.http import Request
    from scrapy.exceptions import DropItem
    
    class MyImagesPipeline(ImagesPipeline):
        def file_path(self, request, response=None, info=None):
            image_guid = request.url.split('/')[-1]
            return 'full/%s' % (image_guid)
    
        #item['image_urls']是我们在item.py中定义的存放图片url的变量,如果定义了别的变量名,注意更换
        def get_media_requests(self, item, info):
            for image_url in item['image_urls']:
                yield Request(image_url)
    
        def item_completed(self, results, item, info):
            image_paths = [x['path'] for ok, x in results if ok]
            if not image_paths:
                raise DropItem("Item contains no images")
            return item
    
  • 修改setting.py

    ITEM_PIPELINES = {
        # 'scrapy.contrib.pipeline.images.ImagesPipeline': 1
        'sis.pipelines.MyImagesPipeline'
        }
    

可以参考stackoverflow上此问题的讨论

原文地址在这里,本文做了一些删除和修改

scrapy如何获取完整的url

发表于 2015-01-29 | 分类于 scrapy

用scrapy抓取网页的时候,有时抓取到的并不是完整的url,而是url的后面部分。例如,完整的url是http://michaelyou.github.io/categories/scrapy/,我们抓到的只是/categories/scrapy/,这个时候如何获取完整的url呢?或者说,如何将完整的url拼出来呢?

解决方法如下:

  • 首先在我们的spider文件头上加上:

    from scrapy.utils.response import get_base_url
    from scrapy.utils.url import urljoin_rfc
    
  • 第二步在我们定义的parse函数中加上:

    #这一句便是取url的开头部分
    base_url = get_base_url(response)
    #省略若干行
    #这一句是取url的后面部分
    relative_url = sel.xpath('***').extract()[0]
    #将两部分合并
    item['link'] = urljoin_rfc(base_url, relative_url)
    

严重提醒:

在求relative_url时,我们在extract()后面加了[0],因为extract()取出来的是一个数组,而函数urljoin_rfc需要的参数是字符串,所以要从数组中将值取出来,这时的数组内只有一个值,index=0,所以加上了[0].很多人忘记了加[0],最后导致合并的时候失败。

笔试题总结4

发表于 2015-01-28 | 分类于 面试

nux的crontab定时执行任务:
基本格式为:

*  *  *  *  *  command

分 时 日  月 周  命令

第1列表示分钟1~59 每分钟用*或者*/1表示

第2列表示小时1~23(0表示0点)

第3列表示日期1~31

第4列表示月份1~12

第5列标识号星期0~6(0表示星期天)

第6列要运行的命令


Class A;
Class B;
void F() {
        A a;
        B b;
}

在函数F中,本地变量a和b的构造函数(constructor)和析构函数(destructor)的调用顺序是:D

A.b构造 a构造 a析构 b析构

B.a构造 a析构 b构造 b析构

C.b构造 a构造 b析构 a析构

D.a构造 b构造 b析构 a析构

解释:按变量声明顺序构造对象,然后入栈
按相反顺序出栈,析构对象。


一棵有124个叶节点的完全二叉树,最多有(B)个节点。

A.247

B.248

C.249

D.250

解释:n0 = n2 + 1,于是度为2的结点个数123个
完全二叉树中度为1结点个数最多1个

因此该完全二叉树中结点最多有123+1+124=248个

最少的话就是度为1的节点是0个,共有节点247个


在排序方法中,元素比较次数与元素的初始排列无关的是(D)

A.Shell 排序

B.归并排序

C.直接插入排序

D.选择排序

解释:选择排序无视队列原来是否有序,他自己都会再排一遍,所以它的时间复杂度一直是最差的O(n2),最好情况和最差情况一样.每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法


给定如下代码: int x[4]={0}; int y[4]={1}; 数组x和y的值为(B)

A.{0,0,0,0},{1,1,1,1}

B.{0,0,0,0},{1,0,0,0}

C.{0,不确定},{1,不确定}

D.与编译器相关


如何判断一个单项链表中是否有环:

解答:
用两个指针,pSlow,pFast,就是一个慢一个快

慢的一次跳一步

快的一次跳两步

往链表末端移动。如果pFast==NULL,则说明链表没有环,如果pSlow==pFast,则说明链表存在环。


-7的二进制补码为:11111001

解释:正数的补码是自身,负数的补码是原码的高位不变,数值位取反加1


中断响应时间是指( C )。

A.从中断处理开始到中断处理结束所用的时间

B.从发出中断请求到中断处理结束所用的时间

C.从发出中断请求到进进中断处理所用的时间

D.从中断处理结束到再次中断请求的时间


在下列排序方法中,不稳定的方法有 C

A.归并排序与基数排序

B.插进排序与希尔排序

C.堆排序与快速排序

D.选择排序与冒泡排序

排序算法总结


下面的排序算法中,初始数据集的排列顺序对算法的性能无影响的是 B

A.插入排序

B.堆排序

C.冒泡排序

D.快速排序

解释:有影响就是这个排序算法最好情况和最差情况的时间复杂度不同。对于无影响,我们只要找最好情况和最差情况时间复杂度一样的算法就可以了,所以是堆排序

笔试题总结3

发表于 2015-01-27 | 分类于 面试

下列关于线程说法错误的是(B)

A.耗时的操作使用线程,提高程序响应

B.耗内存的操作使用线程,提高内存利用率

C.多CPU的系统使用线程,提高CPU利用率

D.并行操作使用线程,如c/s架构中服务端程序为每个客户端请求创建一个线程来响应

解释:多线程可以提高CPU利用率,不能提高内存利用率


个数约为 50k 的数列需要从小到大排序, 数列特征是基本逆序 (多数数字从大到小,个别乱序) ,以下哪种排序算法在事先不了解数列特征的情况下性能大概率最优(不考虑空间限制)_E__.

A.冒泡排序

B.改进冒泡排序

C.选择排序

D.快速排序

E.堆排序

F.插入排序

解释:个数约为50K,基本可以秒杀一般的冒泡,改进冒泡,选择,插入等基本的排序。加上数列的特征是基本逆序,而快速排序的worst case就是基本逆序或者基本有序的情况。综上所述,堆排序应该是大概率最优的。


下列方法中,B__不可以用来程序调优?

A.改善数据访问方式以提升缓存命中率

B.使用多线程的方式提高 I/O 密集型操作的效率

C.利用数据库连接池替代直接的数据库访问

C.利用迭代替代递归

E.合并多个远程调用批量发送

F.共享冗余数据提高访问效率

解释:

A缓存命中率提高,减少访问存储器的开销

B多线程并不能使IO操作并行化。可以通过异步读写、合并读写、计算代替读写等方式优化。 I/O密集型问题一般是硬件层面的问题,比如硬盘,它的I/O就摆在那里,无论你在怎么多线程,瓶颈就在硬盘那,所以B的说法是不可行的。对于“I/O密集型”的应用程序可以采用I/O效率较高的SCSI硬盘,或者采用集群的方式。

C可以减少连接和断开数据库的开销

D可以减少运行时对程序栈进行操作的开销

E减少远程调用的次数


最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是(C)

A.[3,2,5,7,4,6,8]

B.[2,3,5,7,4,6,8]

C.[2,3,4,5,7,8,6]

D.[2,3,4,5,6,7,8]

解释:删除0之后,末尾8代替0的位置


在CPU内存之间进行地址转换时,(B)将地址从虚拟(逻辑)地址空间映射到物理地址空间。

A.TCB

B.MMU

C.CACHE

D.DMA

解释:MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。


#include<stdio.h>
int main()                                                                    
{
    unsigned int a = 6;
    int b = -20;
    (a + b > 6) ? printf(">6\n") : printf("<=6\n");
    printf("%d\n", (a + b));
    return 0;
} 

输出为:>6
解释:执行a+b的时候会将b转换成无符号数,所得的结果也是无符号数,将会是一个很大的正数,自然是大于6的


程序的局部变量存放在栈区,全局变量存放在全局静态区,动态申请的数据存放在堆区


用希尔(Shell)方法排序时,若关键字的初始排序杂乱无序,则排序效率就低(错)

解释:希尔排序又称“缩小增量排序”,即每趟只对相同增量距离的关键字进行比较,这与关键字序列初始有序或无序无关

笔试题总结2

发表于 2015-01-26 | 分类于 面试

下列关于网络编程错误的是?(D)

A.UDP是不可靠服务

B.主动关闭的一端会出现TIME_WAIT状态

C.服务端编程会调用listen(),客户端也可以调用bind()

D.TCP建立和关闭连接都只需要三次握手

E.linux通过提供socket接口来进行网络编程

F.长连接相对短连接可以节省建立连接的时间

解释:D.TCP建立需要3次握手,关闭需要4此握手。无连接的客户端可以调用bind来配置本地信息


Linux系统中某个可执行文件属于root并且有setid,当一个普通用户mike运行这个程序时,产生的进程的有效用户和实际用户分别是A?

A.root mike

B.root rooy

C.mike root

D.mike mike

E.deamon mike

F.mike deamon

解释:setuid的用途是允许普通用户完成一些任务,而完成这些任务需要特权和通常被禁止的访问权限。

在实际应用中,通常用来允许普通用户以root用户的角色运行只有root帐号才能运行的程序或命令。

当一个具有setid访问模式的文件被执行时,进程的有效UID将会变成文件所有者的UID,并使用该UID的访问权限来访问其他的文件和资源。

由于可执行文件属于root,因此当程序设置了setid权限位时,普通用户会临时变成root权限,但实际用户任然是原来的mike。


对于基本有序的序列,按照那种排序方式最快:(B)

A.快速排序

B.冒泡排序

C.归并排序

D.基数排序

解释:而当待排序列已基本有序时,对冒泡排序来说是最好情况,对快速排序来说就是最差情况,而堆排序则最好最差都一样。因此本题答案是冒泡排序。


若一棵二叉树具有10个度为2的结点,5个度为1的结点,则度为0的结点个数是:(B)

A.10

B.11

C.12

D.13

解释:度为0的结点 = 度为2的结点 + 1

在一棵树中,所有点的入度和出度的和相等,下面等式左边为出度,右边为入度(根节点入度为0, 其他节点入度为1)
10 2 + 5 1 = 10 + 5 + x -1

x等于11


上网时候发现网页不能访问,QQ使用正常,出现此问题可能的原因是:B

A.网线问题

B.DNS问题

C.IP地址冲突

D.网关错误

解释:DNS是将域名解析成IP地址的服务。DNS发生问题时无法通过域名访问网页;但是直接通过IP地址连接的应用程序仍可以使用。


二分查找树里查询一个关键字的最坏时间复杂度为(A)。

A.O(n)

B.O(nlogn)

C.O(n^2)

D.O(n^3)

E.O(logn)

F.不确定

解释:二分查找最坏的情况是退化为线性查找,时间复杂度为O(n)


设m和n都是int类型,那么以下for循环语句的执行情况是(A)

for (m = 0, n = -1; n = 0; m++, n++)
     n++;

A.循环体一次也不执行

B.循环体执行一次

C.有限次循环

D.循环结束判断条件不合法

E.运行出错

F.是无限循环

解释:for的条件判断为n = 0,该表达式永远返回0,即for循环不执行。


从逻辑上可以把数据结构分为(C)两大类。

A.动态结构,静态结构

B.顺序结构,链式结构

C.线性结构,非线性结构

D.初等结构,构造型结构

解释:数据结构在存储上(物理上)分为顺序结构和链式结构

在逻辑上分为线性结构(如线性表中的数组,链表)和非线性结构(如:二叉树,图等)


(B)二叉排序树可以得到一个从小到大的有序序列。

A.先序遍历

B.中序遍历

C.后序遍

D.层次遍历

解释:二叉排序树的左子树节点都比该节点小,右子树节点都比该节点打
要得到一个从小到大的有序序列,就要按照 左 中 右的顺序遍历
符合该条件的遍历就是中序遍历


Linux下的进程有哪三种状态?(B)

A.精确态,模糊态和随机态

B.运行态,就绪态和等待态

C.准备态,执行态和退出态

D.手动态,自动态和自由态

解释:
运行状态:这个不用解释了吧,就是正在运行

就绪状态:这个状态等待CPU 时间片的状态,一切准备就绪,随时可以执行,等 CPU 切换到该进程,该进程就会由就绪状态变为运行状态

等待状态:其实就是未就绪状态,还有做一些准备工作或者等待资源


以下关于传输层协议UDP的叙述中正确的(C)

A.比较合适传输小的数据文件

B.提高了高的可靠性

C.提供了高的传输效率

D.使用窗口机制来实现流量控制

A.UDP协议是无连接的,传输效率高,适合传输视频流媒体等大的数据文件

B.UDP无连接特性是降低了可靠性,提高了效率

D.采用窗口机制的协议是TCP


在Bash中,以下哪些说法是正确的(AD)

A.$#表示参数的数量

B.$$表示当前进程的名字

C.$@表示当前进程的pid

D.$?表示前一个命令的返回值

解释:

$# 参数的个数,不包括命令本身.

$$ 目前bash shell的进程编号

$@ 参数本身的列表,也不包括命令本身

$? 上一个命令执行结束后传回值


正则表达式A*B可以匹配(CD)

A.A

B.ACB

C.AB

D.AAB

解释:*表示匹配前一个字符0次或无限次

笔试题总结1

发表于 2015-01-26 | 分类于 面试

应用程序PING发出的是什么报文(C)

A.TCP请求报文

B.TCP应答报文

C.ICMP请求报文

D.ICMP应答报文


语法分析器可以用于(C)

A.识别语义错误

B.识别语法和语义错误

C.识别语法错误

D.识别并修正语法,语义错误

解释:语法分析器通常是作为编译器或解释器的组件出现的,它的作用是进行语法检查、并构建由输入的单片语组成的资料结构


IPV6地址包含多少位()

A.16

B.32

C.64

D.128

解释:IPv6的128位地址通常写成8组,每组为四个十六进制数的形式。比如:
AD80:0000:0000:0000:ABAA:0000:00C2:0002 是一个合法的IPv6地址。


如果在一个建立了TCP连接的socket上调用recv函数,返回值为0,则表示(B)

A.对端发送了一段长度为0的数据

B.对端关闭了连接

C.还没有收到对端数据

D.连接发生错误

解释:recv返回0的唯一条件就是对端关闭了连接
在socket可读的情况下,当有异常发生的时候,read返回-1


以下哪些不是内核对象(D)

A.进程

B.线程

C.互斥器

D.临界区

解释:临界区是资源对象,只能被一个进程访问


同一进程下的多个线程可以共享哪一种资源(B)

A.stack

B.data section

C.register set

D.thread ID

解释:线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。


对于Linux说法,下列说法正确的是()

A.线性访问内存非法时,当前线程会进入信号处理函数

B.用mv命令移动文件时,文件的修改时间会发生变化

C.ulimit -c设置的是函数调用栈的大小

D.malloc函数是应用程序向操作系统申请内存的接口

B.不会改变

C.ulimit用于shell启动进程所占用的资源.-c size:设置core文件的最大值.单位:blocks


在c++中,

const int i = 0; 
int *j = (int *) &i; 
*j = 1; 
printf("%d,%d", i, *j)

输出是多少?(A)

A.0,1

B.1,1

C.1,0

D.0,0

解释:const修饰的常量值具有不可变性,c++编译器通常会对该变量做优化处理,在编译时变量i的值为已知的,编译器直接将printf输出的变量i替换为0。尽管如此,编译器仍然会为变量i分配存储空间,通过修改内存的hack方式将变量i在内存中的值修改后并不影响printf的输出。

gcc不会对此进行优化,所以如果用gcc来编译,输出就是1,1


  struct Date
{
    char a;
    int b;
    int64_t c;
    char d;
};

Date data[2][10]; 在32位系统上,如果Data的地址是x,那么data[1][5].c的地址是(C)

A.X+195

B.X+365

C.X+368

D.X+215

解释:

struct Date {      
    char a;//起始位置0      
    int b;//起始位置4      
    int64_t c;//起始位置8      
    char d;//起始位置16,
    //考虑成员中自身对齐值最大的那个值是8. 16不是8的倍数,所以这个sturct的size是24.  
};

data[1][5]与data[0][0]相隔15个元素,data[1][5].c的地址是x+24*15+(4+4) = 368


快速排序算法在序列已经有序的情况下的复杂度为(B)

A.O(nlogn)

B.O(n^2)

C.O(n)

D.O(n^2 logn)

有序的情况是O(n2);无序的情况是O(nlog2n)

1…141516…18
You Wangqiu

You Wangqiu

世之奇伟、瑰怪,非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也

171 日志
21 分类
24 标签
GitHub 知乎 E-Mail
© 2018 You Wangqiu
由 Hexo 强力驱动
主题 - NexT.Muse