вторник, 27 января 2015 г.

Архивируем и отправляем в компанию "Что делать консалт" каталог STS.
Система исполнения - виндоза!

# -*- coding: utf-8 -*-
from smartgate import sendmail
from sys import exit
import os,zipfile, zlib

PATH2CONS="d:\\Consult"
PATH2ZIP=os.environ.get('tmp')+'\\sts.zip'

if not os.path.exists(PATH2CONS):
   print "Consultant not found at path:"+PATH2CONS
   exit(1)

zf=zipfile.ZipFile(PATH2ZIP,"w",zipfile.ZIP_DEFLATED)
for root,dirs,files in os.walk(PATH2CONS+'\\ADM\\STS'):
   for file in files:
      zf.write(os.path.join(root,file))

zf.close()
if 0==sendmail('192.168.0.1','mymail(собака)mycompany.ru',['blah-blah(собака)konsultant.ru'],"STS VERONIKA","have a fun :)",[PATH2ZIP]):
   os.remove(PATH2ZIP)

понедельник, 12 января 2015 г.

Задача: создаем список случайного размера из случайных элементов состоящих минимум из пяти символов, при чем каждый элемент содержит произвольные буквы (и заглавные и строчные) и цифры. Вывести массив на экран. Исполнение python3

import random
import string

def id_generator(size=6, chars=string.ascii_letters + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

randarray1=[id_generator(random.randint(5,11)) for i in range(random.randint(70,97))]
print ("%s" % list(randarray1[i] for i in range(1,len(randarray1))))



зачем это:
если встретилось уже третий раз, то надо записать - copy/paste проще и быстрее. я ленивый..

вторник, 23 декабря 2014 г.

Задача

Естественно что изучать какой-либо язык интересно не в голой теории, а применяя кое-что из уже изученного. В юности сам себе придумаешь всякую хер.. задачи которые никогда потом не пригодятся. Само собой происходит поиск интересных задач и отсев ненужного шлака. Самые лучшие задачи для изучения - задачи которые нужны и могут быть использованы прямо вот тут и сейчас.

Именно таким образом я и пришел к задаче:
Написать прогу, которая бы заходила на сайт superjob.ru, логинилась, переходила в резюме и на нужном из них обновляла дату публикации. Естественно из консоли. Естественно чтоб можно было её повесить в cron :)

Язык исполнения: python
Инструменты: linux debian + ipython/ipython3, Chrome browser в виндозе для просмотра кодов элементов в режиме отладки, google..куда же без него.

Сходу задача показалась простой, при этом ни каких дополнительных вещей типа try-except или проверки на return из функций я решил не делать - это к сути задачи не относится и при желании может быть добавлено в любое время. Да и с другой стороны - лично я предпочитаю на стадии написания увидеть все возможные трейсбэки своими глазами, а не давить их эксепшенами. Полезно это, имхо.
Для языка python есть множество модулей с уже готовыми api для работы с web-страницами.
Присмотревшись к переписанному из perl'a в python модулю Mechanize родилась вот такая штука:

#!/usr/bin/python

from mechanize import Browser
import socks
import socket


TOR_IP = '127.0.0.1'
TOR_PORT = 9050
URL='http://spb.superjob.ru'
NAME='qwertyu@email.my'
PASSWD='password'

""" на случай если очень захочется пройти инкогнито через tor """
def enable_tor():
        socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, TOR_IP, TOR_PORT)
        socket.socket = socks.socksocket

#enable_tor()
br = Browser()
br.set_handle_robots(False)
br.addheaders = [('User-agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1')]
br.set_handle_equiv(False)
html=br.open(URL)
br.select_form(name='login_form')
br['LoginForm[login]']=NAME
br['LoginForm[password]']=PASSWD
result_page = br.submit()
result_page=br.follow_link(text_regex='Моё резюме')
data = html.read()

И вот тут меня ждало разочарование. Оказывается хитрецы из superjob сделали ссылку обновления даты резюме на javascript. А mechanize ну никак не умеет работать с явой - только классический html. Дальнейшее гугленье мне рассказало про замечательный проект selenium, который умеет и яву и все что угодно потому как использует сторонние веб-движки через webdriver. В частности можно сделать так:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Firefox()
driver.get("http://www.python.org")
assert "Python" in driver.title
elem = driver.find_element_by_name("q")
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
assert "No results found." not in driver.page_source
driver.quit()


Но и здесь кроется засада. Дело в том что для того чтобы юзать webdriver firefox в таком    вот виде, надо чтобы файрфокс был установлен в системе. У меня же вообще нет иксов! Просто debian + консоль. И вообще - это виртуалка на VSphere и ставить иксы туда мне лень и вообще не надо потому как виртуалка для обкатки скриптов. С другой стороны есть возможность скачать любой из поддерживаемых selnium'ом веб-драйверов и попробовать его использовать как RemoteDriver приблизительно вот так 


from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
server_url = "http://%s:%s" % (localhost, 9015)
dc = DesiredCapabilities.HTMLUNIT
wd = webdriver.Remote(server_url, dc)
wd.get('http://www.google.com')


как услужливо подсказывает нам ipython про wbdriver:
from selenium import webdriver

In [141]: webdriver.
webdriver.Opera     webdriver.Android              webdriver.PhantomJS   
webdriver.Chrome  webdriver.Proxy           webriver.Remote  
webdriver.Safari     webdriver.Firefox  


Скачать драйвер Chrome и прицепить его через webdriver.Remote с наскоку не удалось потому как я работаю через ssh, а webdriver.Remote требует в любом случае иксового DISPLAY :0. 

Я по-быстрому забил на эту идею и присмотрелся к PhantomJS. Почему именно он? 

А потому что существует проект WebKit, на котором построены такие браузеры как Safari, Chromium/Crome и иже с ними - много их там. Но отличие PhantomJS в том, что он изначально разрабатывался как консольный вариант, без использования каких-либо GUI.

Ставим:



 apt-get update && apt-get install build-essential chrpath libssl-dev libxft-dev libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev

 cd /tmp && mkdir webkit && cd webkit

 wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.8-linux-i686.tar.bz2

 tar -jxf ./phantomjs-1.9.8-linux-i686.tar.bz2
 cp phantomjs-1.9.8-linux-i686/bin/phantomjs /usr/bin
(обязательно он должен быть на маршруте, иначе работать не будет)

проверяем:

from selenium import webdriver
driver=webdriver.PhantomJS()
driver.get('http://ya.ru')
print driver.title
driver.quit()

Яндекс

Работает! Продолжаем))

Переход по элементам страницы можно организовать как угодно. Я так думаю что придется подтянуть теорию xpath, потому как мне это очень понравилось, хоть я в нем ни бум ни тресь. Именно он (ентон самый xpath) и заменяет де-факто регулярки - так что придется, да и плюс в нете груда иныф про него.
Доступ к элементам я организовал самым простым способом: в лоб))
Еще за замечу, что есть элементы которые в классе находятся в еденичном варианте, а следовательно к таким элементам можно достучаться даже используя find_element_by_class_name
Кстати, а почему бы мне его не применить))

В общем вот что получилось в итоге:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

driver=webdriver.PhantomJS()

driver.get('http://spb.superjob.ru')
#print driver.title - проверим был ли переход можно тут, а можно и try/except

loginform=driver.find_element_by_class_name('LoginForm_link')

loginform.click()
driver.find_element_by_id('LoginForm_login').send_keys('username@mail(dot)domain')
driver.find_element_by_id('LoginForm_password').send_keys('password')
driver.find_element_by_id('LoginForm_password').send_keys(Keys.ENTER)
driver.find_element_by_link_text(u'Моё резюме').click()
driver.find_element_by_link_text(u'обновить дату публикации').click()
driver.quit()



*меня тут прибило искать форму с сабмитом и я использовал тот факт, что клавиша ВВОД обычно и есть "submit", но можно было и через вот это попробовать - это более канонично.
find_element_by_name("submit").click()


Все очень даже просто и понятно.
Задача для развития:
сделать тоже самое используя файл с coockies, Плюс куков в том, что парольи не хранятся в открытом виде, ну и вообще.

http://selenium-python.readthedocs.org/
http://www.ibm.com/developerworks/ru/library/l-python-mechanize-beautiful-soup/
http://wwwsearch.sourceforge.net/mechanize/
http://habrahabr.ru/post/65383/

have a fun
================ UPDATED 13.01.15 ======================
Ребром встал вопрос о сокрытии паролей внутри программы. Даже компиляция в .pyc-файл не очень тут спасает, потому как при просмотре файла внутри .pyc отлично видны такие вещи как username@mail(dot)domain, ну а прикинуть что из текста будет паролем - вообще несложно.
Можно конечно использовать модуль base64, он тоже кодирует, но на глаз сразу видны кодированные строки и раскодировать их вообще "нипраблема". Покопавшись и почитав я принял решение подключить модуль crypto и выбрав какой-либо метод шифрования, хитрый ключ  и размер блока, шифрануть все что надо. Вот что в итоге получилось

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

from Crypto.Cipher import AES
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from base64 import b64decode,b64encode
from sys import exit

""" URL, LOGIN, PASSWD -сгенеренные от ключа numkey http://spb.superjob.ru, login+password"""

numkey=1234567890123456
URL='XmwrgNWXKqkkoRJDgC020R7Uq9RfuJsgey6mcfGYCug='
LOGIN='n5sr2mgr7Kj7cSnp/p1Z8qKbMqsR0VG0Z62fpJB3VrU='
PASSWD='+hS577+JJ1+w91wO7KH4n1Q=='
BLOCK_SIZE = 16
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(b64decode(e)).rstrip(PADDING)
cipher = AES.new(str(numkey))
driver=webdriver.PhantomJS()

# ожидание загрузки страницы зависит от скорости инета/компа..
# да и еще PhantomJS - достаточно тормознутая хрень, делаем неявное ожидание 20 секунд
driver.implicitly_wait(20)

"""
можно по-другому, например до появления како-то элемента
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


driver = webdriver.Firefox()
driver.get('...')

// wait for svg to appear
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.TAG_NAME, 'svg')))
"""

#заключам весь процесс в секцию try/except - неизвестно на каком этапе срубится, может 
#пароль сменим внезапно.. ;)
retcode=0
try:
   driver.get(DecodeAES(cipher,URL))
   loginform=driver.find_element_by_class_name('LoginForm_link')
   loginform.click()
   driver.find_element_by_id('LoginForm_login').send_keys(DecodeAES(cipher,LOGIN))
   driver.find_element_by_id('LoginForm_password').send_keys(DecodeAES(cipher,PASSWD))
   driver.find_element_by_id('LoginForm_password').send_keys(Keys.ENTER)
   driver.find_element_by_link_text(u'Моё резюме').click()
   driver.find_element_by_link_text(u'обновить дату публикации').click()
except:
   retcode=1

driver.quit()
exit(retcode)

скомпилировав это чудо через
import py_compile
py_compile.compile('myfile.py')

я получил вполне достойный псевдобинарник, где нет видных глазу данных. Конечно, я же этот бинарник могу сломать, но не зная как выглядит ключ это нетривиально, что и требовалось.
Ни о какой сверхЪзащите здесь речи и не шло. С другой стороны, для усложнения поиска ключа, итоговый ключ можно было разнести на несколько переменных смешанных со статическими данными. Типа такого:
cr1='.-.+.\./'
cr2=random.randint(1000000000000000,9999999999999999)
cr3='$}887zO`'

размерность ключа должна быть кратной 8ми, а итоговый ключ может выглядеть так

key=cr3+str(random.randint(10000000,99999999))+cr1+cr2
что в .pyc будет выглядеть несовсем понятно.

литература:
https://www.dlitz.net/software/pycrypto/
http://selenium2.ru/docs/webdriver-advanced-usage.html

================ UPDATED 23.04.15 ======================
def superjobupdate(driver, cipher, SJURL, SJLOGIN, SJPASSWD):
   retcode=0
   try:
      driver.get(DecodeAES(cipher,SJURL))
      driver.find_element_by_class_name('LoginForm_link').click()
      driver.find_element_by_id('LoginForm_login').send_keys(DecodeAES(cipher,SJLOGIN))
      driver.find_element_by_id('LoginForm_password').send_keys(DecodeAES(cipher,SJPASSWD)+Keys.ENTER)
      driver.find_element_by_xpath('//*[@id="ng-app"]/div[2]/div/div[2]/div/div[1]/div[3]/div[2]/div[1]/a').click()
      driver.find_element_by_link_text(u'обновить дату публикации').click()
   except:
      retcode=2
   return retcode

def hhupdate(driver, cipher, HHURL, HHLOGIN, HHPASSWD):
   retcode=0
   try:
      driver.set_window_size(1400,1000)
      driver.get(DecodeAES(cipher,HHURL))
      driver.find_element_by_xpath('/html/body/div[3]/div/div[3]/div/div[2]/form/label[1]/input').send_keys(DecodeAES
      driver.find_element_by_xpath('/html/body/div[3]/div/div[3]/div/div[2]/form/label[2]/input').send_keys(DecodeAES
      driver.get(DecodeAES(cipher,HHURL)+'/applicant/resumes')
      arr=driver.find_elements_by_class_name("resumelist__resume")
      maxresumes=len(arr)
      for resumeindex in range(0,maxresumes):
              el=arr[resumeindex].find_element_by_class_name('b-resumelist-vacancyname')
              el.click()
              try:
                 driver.find_element_by_css_selector('body > div.HH-MainContent > div.g-row.m-row_content > div.g-col
              except:
                 pass
              driver.back()
              arr=driver.find_elements_by_class_name("resumelist__resume")
   except:
      retcode=20
   return retcode



четверг, 16 октября 2014 г.

перекладываем еженедельные архивы на другой диск/каталог с созданием маршрутов
скрипт работает из cygwin for windows

#!/bin/sh -x
arr=(недельный месячный)

 for dtype in ${arr[@]}; do
   for i in `ls /cygdrive/g/1c_server_backup/$dtype/torg_b* | gawk -F_ '{print $6"_"$7"_"$8}'`; do
      mkdir "/cygdrive/f/arch/1C папка 2/$dtype/$i";
      mv /cygdrive/g/1c_server_backup/$dtype/*$i* "/cygdrive/f/arch/1C папка 2/$dtype/$i";
      chmod 777 "/cygdrive/f/arch/1C папка 2/$dtype/$i/*"
   done
 done
#read -p "Press [Enter] key to start backup..."

send e-mail with python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import smtplib,sys,os
if sys.version_info<(3,9):
   from email.mime.multipart import MIMEMultipart
   from email.mime.base import MIMEBase
   from email.mime.text import MIMEText
   from email.utils import formataddr
   from email.utils import formatdate
   from email.utils import COMMASPACE
elif sys.version_info<(2,9):
   from email.MIMEMultipart import MIMEMultipart
   from email.MIMEBase import MIMEBase
   from email.MIMEText import MIMEText
   from email.Utils import COMMASPACE, formatdate
   from email import Encoders

def sendmail(server,sender,recipients,subject,bodymessage,attach_files=[],smtp_debug=0):
   """
    sendmail function for sendmail :)
    parameters
        server:string
        sender:string
        recipients:list
        subject:string,
        bodymessage:string
        attach_files: list or tuple of filenames
        smtp_debug:0/1 - to show debug info
   """
   msg = MIMEMultipart()
   msg['From'] = sender
   msg['To'] = COMMASPACE.join(recipients)
   msg['Date'] = formatdate(localtime=True)
   msg['Subject'] = subject
   msg.attach(MIMEText(bodymessage))
   for f in attach_files:
       part = MIMEBase('application', "octet-stream")
       part.set_payload( open(f,"rb").read() )
       Encoders.encode_base64(part)
       part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
       msg.attach(part)
   try:
     smtpObj = smtplib.SMTP(server)
     smtpObj.set_debuglevel(smtp_debug)
     smtpObj.sendmail(sender, recipients, msg.as_string())
     smtpObj.close()
   except SMTPException:
    return 1
   return 0

if __name__ == '__main__':
   if 0==sendmail('10.10.10.10','from@dom.foo',['to1@dom.foo','to2@dom.foo'],'ololo subject','Hello WORLD!\n',['/tmp/myfile1','/tmp/myfile2']):.
    print "All ok!"
   else:
Пакетник для скачивания по фтп c очисткой старых

set OLDPATH=%cd%
f:

Set TODAYSDATE=%date:~6,8%%date:~3,2%%date:~0,2%

cd  f:\1C-MSK\buh_w\
wget -c --no-remove-listing --ftp-user=USER --ftp-password=PASSWORD ftp://192.168.5.101/Buh_w/*%TODAYSDATE%*.bak
for %%f in (*.bak) do c:\progra~1\winrar\winrar m -tl -m2 "%%f".rar "%%f"
forfiles.exe /D -5 /P . /S /M *.rar /C "cmd /c del @file | more"

cd %OLDPATH%

понедельник, 29 сентября 2014 г.

example routes parsing with python on windows

Задача такая: при подключении к windows vpn надо выловить новое подключение и сделать через него роут на левый ip-шник
Условия: windows! vpn выдает произвольный адрес из сети 192.168.6.0/24
Доп. условие: скрипт должен удалять свое же подключение. Тут легче - ip уникальный.
В принципе, скрипт приурочен к изучению python (отличный язык, имхо)


#by alex. 2014
#-*- coding: cp1251 -*-
import codecs,sys
from os import _exit
from subprocess import check_output

outf = codecs.getwriter('cp866')(sys.stdout, errors='replace')
sys.stdout = outf

route_print=check_output("route print")
routes=route_print.split("\n")

if "192.168.20.21" in route_print:
  if "Y"==raw_input(u"Маршрут уже присутствует!\nХотите удалить?Y/N:").upper():
    check_output("route delete 192.168.20.21")
_exit(0)

resultroute=[]
for i in routes:
   if "255.255.255.255" in i and "192.168.6" in i:
     resultroute=i.split()

if len(resultroute)==0:
 raw_input(u"Не обнаружено подключение! Нажмите ввод для выхода")
 _exit(1)

try:
 check_output("route add 192.168.20.21 mask 255.255.255.255 " + resultroute[3])
except:
 raw_input(u"Маршрут не применен!\nНажмите ВВОД для выхода")
 raise

raw_input(u"Маршрут применен. Нажмите ввод для выхода")