Python: RSS to Twitter

29 июня 2009 2 комментария

С недели 2 назад для одного хитрого сайта понадобилось сделать автоматический репост статей в твиттер. Решил не писать на php, а попробовать на python’е наваять. Откровенного говоря очень понравилось! Получилось очень просто и легко.

Написал наверное за пол часа, что с учетом того, что python я вообщем то не знаю совсем не плохо.

  1. #!/usr/local/bin/python
  2. # -*- coding: utf8 -*-
  3.  
  4. import urllib
  5. import libxml2
  6. import pickle
  7. import twitter
  8. import os
  9. from time import sleep
  10.  
  11. bit_api="bit.ly.api"
  12. bit_login="bit.ly.login"
  13. twit_login="name"
  14. twit_pass="pass"
  15. rss_url="http://feed"
  16.  
  17.  
  18. def shorten_url(long_url, login_user, api_key):
  19.     try:
  20.         longUrl = urllib.urlencode(dict(longUrl=long_url))
  21.         login = urllib.urlencode(dict(login=login_user))
  22.         apiKey = urllib.urlencode(dict(apiKey=api_key))
  23.  
  24.         encodedurl="http://api.bit.ly/shorten?version=2.0.1&%s&%s&%s" % (longUrl, login, apiKey)
  25.  
  26.         request = urllib.urlopen(encodedurl)
  27.         responde = request.read()
  28.         request.close()
  29.         responde_dict = eval(responde)
  30.         short_url = responde_dict["results"][long_url]["shortUrl"]
  31.         return short_url
  32.     except IOError, e:
  33.         raise "urllib error "
  34.  
  35.  
  36. if __name__ == '__main__':
  37.     doc = libxml2.parseFile(rss_url)
  38.     links=[]
  39.     for item in doc.xpathEval('//channel/item/link'):
  40.         links.append(item.content)
  41.  
  42.     titles=[]
  43.     for item in doc.xpathEval('//channel/item/title'):
  44.         titles.append(item.content)
  45.  
  46.     rss_path = os.path.join(os.path.dirname(__file__), 'rss.db')
  47.     file = open (rss_path)
  48.     old_links=pickle.load(file)
  49.     file.close()
  50.  
  51.     items=[]
  52.     f=0
  53.     for x in xrange(0,len(links)):
  54.         for old in old_links:
  55.             f=0
  56.             if (old==links[x]):
  57.                 f=1
  58.                 break
  59.         if (f==0):
  60.             items.append(x)
  61.  
  62.     api = twitter.Api(username=twit_login, password=twit_pass)
  63.  
  64.     for x in items:
  65.         title=titles[x].decode("utf8")
  66.         if len(title)<=120:
  67.             print "link %s: %s" % (x,links[x])
  68.             if x!=0:
  69.                 sleep(15)
  70.             api.PostUpdate(title+" "+shorten_url(links[x], bit_login, bit_api))
  71.  
  72.     file=open(rss_path,"w")
  73.     ser=pickle.dump(links,file)
  74.     file.close()
  75.  

Какие трудности вылезли в процессе:

1. Нужно как-то было не постить то что уже отправлено. Решилось сохранением списка ссылок в сериализованном виде в файл

2. Размер сообщение в твиттере не больше 140 символов, а надо бы в твитты еще и ссылки на статьи вставлять, притом что заголовки у статей я бы сказал не маленькие. Решилось регистрацией на bit.ly (сервис укорачивания ссылок) и собственно укорачивание. Не помню уже сам написал или с сайта их взял функцию, ибо там все так просто что даже неинтересно.

3. Невероятно, но встала проблема с кодировкой. Решилось за 5 минут гугления и пробования нескольких комбинаций. Что-то не понимал я откуда куда функции перекодируют. Вобщем вот так надо

title.decode(«utf8″)

4. Ну и задержку пришлось поставить, иначе bit.ly говорило зайдите позже :)

Дальше все это безобразие запихал в крон:

*/12 * * * * /usr/local/bin/python /usr/local/www/rss2twitter/rss2twit.py

За время пока это дело работает, уже сделан репост 1256 твиттов, так что красота.

А вообще все очень просто и легко, особенно учитывая что twitter api уже написано. хотя если бы его небыло, думаю я бы просто логинился, сохранял куки и постил обычным POST запросом.

Ну и файлик со скриптом на последок: rss2twit.tar.gz

З.Ы. с удовольствием выслушаю критику, потому как первое не Hello, world! приложение на python’е. :)

Хозяйке на заметку: замечания детям от посторонних

27 июня 2009 4 комментария

На баше вычитал, надо будет попробовать

Моему ребенку делают замечания посторонние люди! Как реагировать?
Обучите ребенка волшебному заклинанию: «Меня мама учит, что не всякое оценочное суждение должно служить модификатором поведения». Будучи произнесено с четкой дикцией и доброжелательно-уверенной интонацией оно действует аналогично заклинанию: «Окаменей!», причем более надежно, хотя и ненадолго, и без опасных побочных эффектов.

Думаю действительно сработает :)

Монтирование флешки под FreeBSD

1 июня 2009 5 комментариев

Заколебался каждый раз вспоминать как монтировать флешки под фрей, решил записать:

mount_msdosfs -Lru_RU.KOI8-R /dev/da0s1 /mnt/flash

Яндекс vs Google или устами младенца…

10 мая 2009 3 комментария

Сейчас мелкий (8 месяцев) сидел и колошматил рученками по клавиатуре ноутбука и водил пальчиками по тачпаду, я чтоб время зря не пропадало, решил показать ему картинки в яндексе, набрал куку, и показал как искать. Мелкий посмотрел, 10 секунд активно колошматил по клавиатуре, и вуаля, тот же запрос в гугел.картинках!!!

вот такие пироги :) ))

Ubuntu на Asus X55SV и звук в наушники

Давеча (месяц уже) поставил убунту на бук, вроде все было нормально, но тут обнаружился странный косяк, при подключении наушников нифига не отключали встроенные колонки, что прямо скажем не комильфо.

Немного порыскав в интернете, нашел решение. Нужно в файлик/etc/modprobe.d/alsa-base.conf добавить строчечку:

options snd-hda-intel model=lenovo-ms7195-dig

после чего перезагрузился и все заработало :)

PS. как вариант предлагалась еще вот такая строчка:

options snd-hda-intel model=3stack-6ch

PPS. пользуясь случаем попиарю блог товарища seo-оптимизатора, мне пофик, а ему приятно буит :)

Перевед rss feed блога на feedburner

Собственно сабж, долго подбирал пароль к старому аккаунту feedburner, чтобы перенести свои фиды в аккаунт на гугле (для меня было откровением, что теперь фидбернер под крылом гугла, продались чтоль?), в итоге подобрал, перенес.

Раньше фид ссылался на блог на блоггере, а теперь собственно ссылается на этот блог.

Редирект фида на сайте сдела с помощью вот этого плагина: http://wordpress.org/extend/plugins/feedburner-plugin/, не смотря на то что был рекомендован другой, ну не заладилось что-то с ним, или я чего-то не понял или не работает оно, хотя сделал все по инструкции.

Вообщем кто не понял, теперь адрес фида вот такой: http://feeds.feedburner.com/mrdaark :)

Пользуйтесь на здоровье. :)

Нетбук в семью :)

Сегодня заказал для жены вот такого красавца:

Samsung NC10-KA01

Это Samsung NC10-KA01, неплохой такой нетбук с заявленным максимальным временем работы 8 часов.

по характеристикам:

Процессор Intel Atom N270 (1.60 ГГц, FSB 533 МГц, L2 cache 512 Кб)
Набор микросхем Intel 945GSE Express
Оперативная память 1024 Мб DDR2 (до 1,5 Гб)
Дисплей 10.2″WSVGA (1024×600), неглянцевый, LED подсветка
Видеосистема Intel GMA 950 128 Мб (до 384 Mб памяти)
Жесткий диск 160 Гб
Картридер 3-в-1 (SD, SDHC, MMC)
Звук Intel High-Definition audio, Эффект EDS (эффект улучшения цифрового звука), Стереодинамики 3 Вт (1,5 Вт x 2)
Порты 3 x USB 2.0, line-out, mic-in, RJ-45, D-SUB,
Связь
  • Cеть: 10/100 Mbps Ethernet
  • WiFi: Atheros 802.11b/g
  • Bluetooth 2.0 + EDR
Питание 6-элементный батарейный блок (Li-ion)
Время работы До 8 ч
Особенности
  • Встроенная цифровая камера 1.3 Mпикс
  • Предустановленное ПО:
    • Samsung Recovery Solution III
    • Samsung Magic Doctor
    • Samsung Update Plus
    • Easy Display Manger
    • Easy Battery Manager
    • Easy Network Manager
    • Easy SpeedUp Manager
    • McAfee SecurityCenter
    • Adobe Acrobat Reader
Операционная система Windows XP Home Edition
Размеры, мм 261×185,5×31,8
Вес, г 1330

Вообщем практически все как обычно. Радуют отзывы о клавиатура, ей поют всяческие дифирамбы.

Поначалу смотрел в сторону MSI Wind U120, но как то пока искал где его заказать, наткнулся на обзор самсунга и он меня прям таки обрадовал. Вообщем заказа его на озоне, вместе с доставкой получилос 16.418 рублей, вообщем нормально, если учесть что до этого смотрел в местных магазинах нетбуки от 16 до 23тыр. Теперь ждем-с…

Только тсссс, жене сюрприз. :)

MySQL: базу знаю :)

Сегодня, не то чтобы от нечего делать, а так, прошел курс на сайте http://www.intuit.ru/ «Введение в СУБД MySQL». Прошел так, чтобы себя проверить, а то совсем самоучка, а тут хоть какое-то структурирование знаний. Честно скажу практически ничего нового для себя не вынес, пройти было на удивление легко, с первым тестом повозился (там теоретические знания по СУБД), никак он мне отлично ставить не хотел, все хорошо да хорошо. :) )

Но вообщем все остальные, направленные на практику, сдал без проблем и сразу на отлично, вообщем-то как и экзамен. Местами правда у них в вопросах неоднозначные ответы, зависящие от нераскрытых входных данных, но это фигня :)

Cертификат даже себе выписал (правда только электронный). Посмотреть можно по адресу: http://www.intuit.ru/diploma/lQ2wte8s3eAw/P00057527/. Вообщем штука прикольная, надо будет поискать еще курсов, может чего дельного для себя найду, заодно и экспиренс прокачаю :)

Миграция на jabber: начало пути

13 марта 2009 23 комментария

Недавеча как недавно мой ноутбук отправился в сервисный-центр, что-то вдруг приводу вздумалось не писать диски, я обиделся после 6го запоротого и отнес.

А посколько как-то профиль аськин скинуть не догадался, то решил попробовать перейти на джаббер, заодно посмотреть как пипл на это отреагирует.

Вообщем скачал миранду, поставил на богом забытом сервере, настроил в ней автоответчик, примерно с таким сообщением: «В связи с тем, что мой ноутбук отдыхает в сервисном центре и профиль icq мне недоступен, найти меня можно в jabber: i@mrdaark.com.»

Ну повесил, включил gtalk и сижу довольный что мне никто не пишет. На третий день стало как-то не по себе, и решил я проверить, а чего это, может не работает оно… как выяснилось, действительно, не работает. Т.к. jid у меня не напрямую от гугла, а через мой домен, то откуда же внешние сервера могут узнать где это у меня jabber сервер, по адресу то оно не отвечает.

Но нет худа без добра, полез в справку гугла, и нашел там информашку о том, как же все таки научить все это безобразие правильно работать. Как выяснилось достаточно всего лишь прописать кучку srv записей к домену:

_xmpp-server._tcp.mrdaark.com. IN SRV 5 0 5269 xmpp-server.l.google.com.
_xmpp-server._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server1.l.google.com.
_xmpp-server._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server2.l.google.com.
_xmpp-server._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server3.l.google.com.
_xmpp-server._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server4.l.google.com.
_jabber._tcp.mrdaark.com. IN SRV 5 0 5269 xmpp-server.l.google.com.
_jabber._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server1.l.google.com.
_jabber._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server2.l.google.com.
_jabber._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server3.l.google.com.
_jabber._tcp.mrdaark.com. IN SRV 20 0 5269 xmpp-server4.l.google.com.

Не забыв конечно увеличить id зоны, дабы оно обновилось на родительских dns-серверах.
Вообщем через пару минут я сумел отправить сообщение из вне на мой jid, а еще через пару часов и в обратном направление.

Правда что прикольно мне так до сих пор никто и не написал, или народ решил не запариваться и тупо подождать когда у меня ноут заработает или одно из двух. :) )

Изобретая велосипеды: PHP шаблонизатор

13 февраля 2009 6 комментариев

Началось все с того что мне не нравятся существующие темплейтеры типа smarty. Не прет.

Написал свой, простенький, но для повседневности хватает.

вот такой вот класс:

  1. <?php
  2. class Template {
  3.     private $template;
  4.     private $vars;
  5.     private $blocks;
  6.     private $res;
  7.     private $registry;
  8.  
  9.     function __construct($registry) {
  10.         $this->registry = $registry;
  11.     }
  12.  
  13.     private function getvars($block="")
  14.     {
  15.         $str="";
  16.         if ($block=="")
  17.         {
  18.             $str=$this->res;
  19.         }
  20.         else
  21.         {
  22.             if (isset($this->blocks[$block]))
  23.             {
  24.                 $str=$this->blocks[$block];
  25.             }
  26.         }
  27.         $m=array();
  28.         preg_match_all("/{%(.*)?%}/ismU",$str,$m,PREG_SET_ORDER);
  29.         return $this->assoc($m);
  30.     }
  31.  
  32.     private function assoc($arr)
  33.     {
  34.         $r=array();
  35.         if (count($arr)>0)
  36.         {
  37.             foreach ($arr as $v)
  38.             {
  39.                 $r[$v[1]]=$v[0];
  40.             }
  41.         }
  42.         return $r;
  43.     }
  44.  
  45.     private function getblocks()
  46.     {
  47.         $m=array();
  48.         preg_match_all("/<!--(.*)?-->.*?<!--\/\\1-->/ismU", $this->template, $m ,PREG_SET_ORDER);
  49.         $this->blocks=$this->assoc($m);
  50.     }
  51.  
  52.     private function getinclude($data)
  53.     {
  54.         $m=array();
  55.         preg_match_all("/<!--include:(.*)?-->/ismU", $this->template, $m ,PREG_SET_ORDER);
  56.         foreach ($m as $i)
  57.         {
  58.             $t=new template($this->registry);
  59.             $this->res=str_replace($i[0],$t->compile($data,site_path.$i[1]),$this->res);
  60.             unset($t);
  61.         }
  62.     }
  63.  
  64.     function getphp()
  65.     {
  66.         $m=array();
  67.         preg_match_all("/{--(.*)?--}/ismU", $this->res, $m ,PREG_SET_ORDER);
  68.         $out="";
  69.         foreach ($m as $p)
  70.         {
  71.             ob_start();
  72.             eval ("echo ".$p[1].";");
  73.             $out = ob_get_clean();
  74.             $this->res=str_replace($p[0],$out,$this->res);
  75.         }
  76.     }
  77.  
  78.     private function setvars($tpl,$vars,$d)
  79.     {
  80.         $str=$tpl;
  81.         foreach ($vars as $var => $search)
  82.         {
  83.             if (isset($d[$var]))
  84.             {
  85.                 $str=str_replace($search,$d[$var],$str);
  86.             }
  87.             else
  88.             {
  89.                 $str=str_replace($search,"",$str);
  90.             }
  91.         }
  92.         return $str;
  93.     }
  94.  
  95.     private function compileblock($block,$data)
  96.     {
  97.         $tpl=$this->blocks[$block];
  98.         $res="";
  99.         $vars = $this->getvars($block);
  100.         //print_r($vars);
  101.         foreach($data as $d)
  102.         {
  103.             $res.=$this->setvars($tpl,$vars,$d)."\n\n";
  104.         }
  105.         $this->res=str_replace($tpl,$res,$this->res);
  106.     }
  107.  
  108.     function compile($data,$tplfile)
  109.     {
  110.         $this->template=file_get_contents($tplfile);
  111.         $this->res=$this->template;
  112.         $this->getblocks();
  113.         foreach ($this->blocks as $block => &$v)
  114.         {
  115.             if (isset($data[$block]))
  116.             {
  117.                 $this->compileblock($block,$data[$block]);
  118.             }
  119.             else
  120.             {
  121.                 $this->res=str_replace($this->blocks[$block],"",$this->res);
  122.             }
  123.         }
  124.         $this->res=$this->setvars($this->res,$this->getvars(),$data);
  125.         $this->getinclude($data);
  126.         $this->getphp();
  127.  
  128.         return $this->res;
  129.     }
  130.  
  131.     function html($data,$tplfile)
  132.     {
  133.         echo $this->compile($data,site_path.$tplfile);
  134.     }
  135. }
  136. ?>

Чтобы было проще понять, вот пример шаблона:

  1. <!--include:tpl/header.tpl-->
  2. <table class="adminlist" style="width:300px;">
  3. <tr>
  4. <th class="title" width="3%"><input name="toggle" id="toggle" value="1" onclick="checkAll(1);" type="checkbox" /></th>
  5. <th class="title" width="97%">&nbsp;</th>
  6. </tr>
  7.  
  8. <!--users-->
  9. {--('1'=='{%flag%}') ? '<tr><td colspan="2">{%gname%}</td></tr>':'' --}
  10. <tr class="row0">
  11. <td><input type="checkbox" class="checkbox" name="check_{%id%}" /></td>
  12. <td><a href="/users/edit/{%id%}">{%name%}</a></td></tr>
  13. <!--/users-->
  14.  
  15. {--('write'=='{%right%}') ? '<tr><td colspan="6" style="text-align:center;"><a href="/users/create">Добавить</a></td></tr>':''--}
  16. </table>
  17.  
  18. <!--include:tpl/footer.tpl-->

Как использовать? примерно вот так:

  1. <?php
  2. $d=array();
  3. $d['users'][1]['id']=1;
  4. $d['users'][1]['name']='User 1';
  5. $d['users'][1]['game']='Group 1';
  6. $d['users'][1]['flag']=1;
  7. $d['users'][2]['id']=2;
  8. $d['users'][2]['name']='User 2';
  9. $d['users'][2]['game']='Group 1';
  10. $d['users'][2]['flag']=0;
  11. $d['users'][3]['id']=3;
  12. $d['users'][3]['name']='User 3';
  13. $d['users'][3]['game']='Group 2';
  14. $d['users'][3]['flag']=1;
  15. $d['right']='right';
  16.  
  17. $template = new Template($registry);
  18. $template->html($d,'tpl/usersform.tpl');
  19.  
  20. ?>

Поподробнее про теги шаблона:
{%name%} – переменная
<!–include:file.tpl–> – включение внешнего шаблона (количество включений ограничено только оперативной памятью :)
повторяющиеся блоки оформляются так
<!–blockname–>
{%name%}
<!–/blockname–>
где blockname – это массив значений в передаваемом параметре
условия задаются вот так:
{–(’1′==’{%flag%}’) ? ‘{%var1%}’:'{%var2%}’ –}

ссылочка на файлик класса: template.phps