Accessing and debugging the Django development server from another machine

In continuing my series about setting up a Linux virtual machine for Django development (see parts one and two), I wanted to share another tip for accessing the VM from the host machine.

Set up development server to listen to external requests

By default, when using the Django management runserver command, the development server will only listen to requests originating from the local machine (using the loopback address 127.0.0.1). Luckily, the runserver command accepts an IP address and port. Specifying 0.0.0.0 will allow the server to accept requests from any machine:

python manage.py runserver 0.0.0.0:8000

I do not have to worry about security issues with the development server listening to all requests, since the VM is protected from external access by a firewall.

Set up server to send debug information to local network

While the first step will allow us to access the development server from the host machine, we will not be able to see debugging information (for example, thedjango-debug-toolbar will not be displayed on the host machine, even if DEBUG is set to True). Django uses another setting, INTERNAL_IPS, to determine which machines are allowed to view debugging information.

For a typical installation, I set INTERNAL_IPS to only specify 127.0.0.1, allowing me to easily debug Django apps running on the local machine.

INTERNAL_IPS = ('127.0.0.1',)

Now, since the setting is a tuple, we could easily add the IP address of the host machine and call it a day. We would run into the same problem as the last post, which is the reason I installed Samba in order to access the VM using its host name instead of IP address. Whenever the machine gets a new IP address from the DHCP server, I would have to update the setting. I found a great solution to this problem in this snippet, which adds wildcard support to INTERNAL_IPS. I personally put this code inside an if block, so that it only executes in a development setting. Here is the code from the end of my settings.py file:

if DEBUG:

    from fnmatch import fnmatch
    class glob_list(list):
        def __contains__(self, key):
            for elt in self:
                if fnmatch(key, elt): return True
            return False

    INTERNAL_IPS = glob_list(['127.0.0.1', '192.168.*.*'])

Now, I can access and debug Django projects using the development server from any machine on my local network.

How to Install and Connect to PostgreSQL on CentOS 7

postgresqlPostgreSQL (pronounced ‘post-gres-Q-L’) is a free, open-source object-relational database management system (object-RDBMS), similar to MySQL, and is standards-compliant and extensible. It is commonly used as a back-end for web and mobile applications. PostgreSQL, or ‘Postgres’ as it is nicknamed, adopts the ANSI/ISO SQL standards together, with the revisions.

Pre-Flight Check
  • These instructions are intended specifically for installing PostgreSQL on CentOS 7.
  • I’ll be working from a Liquid Web Self Managed CentOS 7 server, and I’ll be logged in as root.
Step 1: Add the PostgreSQL 9.3 Repository

In this case we want to install PostgreSQL 9.3 directly from the Postgres repository. Let’s add that repo:

wget http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/pgdg-centos94-9.4-1.noarch.rpm
rpm -ihvU pgdg-centos94-9.4-1.noarch.rpm

Step 2: Install PostgreSQL

First, you’ll follow a simple best practice: ensuring the list of available packages is up to date before installing anything new.

yum -y update

Then it’s a matter of just running one command for installation via apt-get:

yum -y install postgresql94 postgresql94-server postgresql94-contrib postgresql94-libs --disablerepo=* --enablerepo=pgdg94

PostgreSQL should now be installed.

Step 3: Start PostgreSQL

Configure Postgres to start when the server boots:

systemctl enable postgresql-9.4

Start Postgres:

/usr/pgsql-9.4/bin/postgresql94-setup initdb

systemctl start postgresql-9.4

Step 4: Switch to the Default PostgreSQL User

As part of the installation Postgres adds the system user postgres and is setup to use “ident” authentication. Rolesinternal to Postgres (which are similar to users) match with a system user account.

Let’s switch into that system user:

su – postgres

And then connect to the PostgreSQL terminal (in the postgres role):

$ psql

That’s it! You’re connected and ready to run commands in PostgreSQL as the postgres role.

Colored Bash man pages and Log files

colored_bash

Below you can find bash codes which you can use with man pages or while reading log files:

'\e[0;30m' # Black - Regular
'\e[0;31m' # Red
'\e[0;32m' # Green
'\e[0;33m' # Yellow
'\e[0;34m' # Blue
'\e[0;35m' # Purple
'\e[0;36m' # Cyan
'\e[0;37m' # White

'\e[1;30m' # Black - Bold
'\e[1;31m' # Red
'\e[1;32m' # Green
'\e[1;33m' # Yellow
'\e[1;34m' # Blue
'\e[1;35m' # Purple
'\e[1;36m' # Cyan
'\e[1;37m' # White

'\e[4;30m' # Black - Underline
'\e[4;31m' # Red
'\e[4;32m' # Green
'\e[4;33m' # Yellow
'\e[4;34m' # Blue
'\e[4;35m' # Purple
'\e[4;36m' # Cyan
'\e[4;37m' # White

'\e[40m'   # Black - Background
'\e[41m'   # Red
'\e[42m'   # Green
'\e[43m'   # Yellow
'\e[44m'   # Blue
'\e[45m'   # Purple
'\e[46m'   # Cyan
'\e[47m'   # White
'\e[0m'    # Text Reset

If you are using less application for viewing man pages you can add below variables to your .bashrc file. After that reload .bashrc variables with command: source /home/user/.bashrc and you will see colored man pages.

export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;31m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;44;33m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;32m'

For log files use perl syntax with your combined regex:

 tail -f  /var/log/mail.log | perl -pe 's/(fatal|error|panic|success)/\e[1;31m$&\e[0m/g'

Last colored trick. Use grep command with —color switch.

Dig

digDig is a powerful Linux tool and today I’ll demonstrate some useful everyday examples including a reverse lookup, zone transfer, and how to find the SOA (start of authority) in a zone file.

So what is dig?

man dig

«dig (domain information groper) is a flexible tool for interrogating DNS name servers.»

A simple example

How to find the IP address (A record) associated with a domain:

dig tomhayman.co.uk +short

Which outputs:

75.127.99.28

Reverse lookup example

How to find the domain name associated with an IP address:

dig -x 75.127.99.28 +short

Which outputs:

zoe.asmallorange.com.

(For more information remove +short)

Zone transfer example

First, find the name server to query:

dig ns tomhayman.co.uk +short

Which outputs:

ns1.asmallorange.com.
ns2.asmallorange.com.

Then:

dig -t axfr @ns1.asmallorange.com tomhayman.co.uk

Which outputs:

; <<>> DiG 9.3.4-P1 <<>> -t axfr @ns1.asmallorange.com tomhayman.co.uk
; (1 server found)
;; global options:  printcmd
; Transfer failed.

But the transfer failed!  This is normally due to security settings on the name server.  Sometimes you can request this to be removed, although most providers prevent it.

However, some organisations allow this behaviour.  One of them is Wikipedia

So if we try the process again:

dig ns wikipedia.org +short

Which outputs:

ns0.wikimedia.org.

Then:

dig -t axfr @ns0.wikimedia.org wikipedia.org | head -n 10

Which outputs:

; <<>> DiG 9.3.4-P1 <<>> -t axfr @ns0.wikimedia.org wikipedia.org
; (1 server found)
;; global options:  printcmd
wikipedia.org.          86400   IN      SOA     ns0.wikimedia.org. hostmaster.wikimedia.org. 2010082803 43200 7200 1209600 3600
wikipedia.org.          3600    IN      A       208.80.152.2
wikipedia.org.          86400   IN      NS      ns0.wikimedia.org.
wikipedia.org.          86400   IN      NS      ns1.wikimedia.org.
wikipedia.org.          86400   IN      NS      ns2.wikimedia.org.
wikipedia.org.          3600    IN      MX      50 lists.wikimedia.org.

(N.B. I used head to output the first 10 lines only as wikipedia.org has thousands of CNAME’s)

Start of authority (SOA) example

Find the SOA record in a zone file:

dig +nocmd wikipedia.org any +multiline +noall +answer

Which outputs:

wikipedia.org.          1589 IN A 208.80.152.2
wikipedia.org.          84389 IN NS ns0.wikimedia.org.
wikipedia.org.          84389 IN SOA ns0.wikimedia.org. hostmaster.wikimedia.org. (
2010082803 ; serial
43200      ; refresh (12 hours)
7200       ; retry (2 hours)
1209600    ; expire (2 weeks)
3600       ; minimum (1 hour)
)
wikipedia.org.          1589 IN MX 50 lists.wikimedia.org.

Dig can do a lot more than the examples I’ve illustrated today.  You can build some useful scripts with it too, which I’ll demonstrate at another time.

Конвертация БД из CP1251 в UTF8

Конвертацию БД из Win-1251 в UTF8 можно произвести разными способами, но самый быстрый и простой — использование SQL-запроса, приведенного ниже.

ALTER TABLE `db_name`.`table_name` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

При помощи этого запроса, можно конвертировать таблицу базы данных в любую, доступную в MySQL кодировку. Но что делать, если таблиц 100, 200 или больше, и все таблицы необходимо перекодировать в UTF8 из Win-1251? Для решения этой проблемы, можно отправить в MySQL запрос, который сгенерирует необходимые SQL-запросы для всех таблиц БД. При использовании PHPMyAdmin останется только скопировать результаты и запустить их как SQL-запрос:

SELECT CONCAT( 'ALTER TABLE `', t.`TABLE_SCHEMA` , '`.`', t.`TABLE_NAME` , '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;' ) AS sqlcode
FROM `information_schema`.`TABLES` t
WHERE 1
AND t.`TABLE_SCHEMA` = 'My_DB_for_convert'
ORDER BY 1
LIMIT 0 , 90

Этот запрос будет работать в MySQL версии 5 и выше. My_DB_for_convert — следует заменить на имя БД, таблицы в которой необходимо конвертировать в UTF-8.

utf8_general_ci или utf8_unicode_ci

utf8_general_ci и utf8_unicode_ci отличаются только скоростью работы и порядком сортировки. Поскольку utf8_general_ci работает быстрее — это и есть предпочтительный выбор. Подробнее, про отличия utf8_general_ci и utf8_unicode_ci можно прочитать в официальной документации MySQL. В случаее необходимости, способом описанным выше, можно будет изменить подкодировку UTF8 c utf8_general_ci на utf8_unicode_ci.

MongoDB + Django

mongodb
mongoDB — документо-ориентированная система управления базами данных (СУБД) с открытым исходным кодом, не требующая описания схемы таблиц. Написана на языке C++ и распространяется в рамках лицензии Creative Commons.

В последнее время становится довольно популярной и восстребованой. И вот возникла идея использовать ее в связке с фреймворком Django. Собственно о чем далее и пойдет речь.

Для решения поставленной задачи мы будем использовать приложение mongodb-engine. Данное приложение тесно связано еще с несколькими приложениями, установкой которых мы и займемся вначале.

Django-nonrel — используется для поддержки NoSQL в Django.

pip install hg+https://bitbucket.org/wkornewald/django-nonrel

djangotoolbox — набор инструментов для работы с нереляционными базами данных, лишним не будет.

pip install hg+https://bitbucket.org/wkornewald/djangotoolbox

А теперь уже ставим и mongodb-engine:

pip install git+https://github.com/django-nonrel/mongodb-engine

Указываем нашу базу данных в settings:

DATABASES = {
   'default' : {
      'ENGINE' : 'django_mongodb_engine',
      'NAME' : 'my_database'
   }
}

При необходимости также можно указать host, port, user, password.

Данное приложение предоставляет два типа полей для хранения произвольных данных, не входящих в стандартную django модель.

ListField

Списки и им подобные, представление массивов в формате BSON

from djangotoolbox.fields import ListField

class Post(models.Model):
    ...
    tags = ListField()
>>> Post(tags=['django', 'mongodb'], ...).save()
>>> Post.objecs.get(...).tags
['django', 'mongodb']

Вариант с указанием типа:

class Post(models.Model):
    ...
    edited_on = ListField(models.DateTimeField())
>>> post = Post(edited_on=['1010-10-10 10:10:10'])
>>> post.save()
>>> Post.objects.get(...).edited_on
[datetime.datetime([1010, 10, 10, 10, 10, 10])]

Данный тип поля удобно использовать для организации связи один-ко-многим:

from djangotoolbox.fields import EmbeddedModelField, ListField

class Post(models.Model):
    ...
    comments = ListField(EmbeddedModelField('Comment'))

class Comment(models.Model):
    ...
    text = models.TextField()

EmbeddedModelField — используется для организации связей между моделями.

DictField

Второй тип поля DictField, который используется в BSON для обьектов.

from djangotoolbox.fields import DictField

class Image(models.Model):
    ...
    exif = DictField()
>>> Image(exif=get_exif_data(...), ...).save()
>>> Image.objects.get(...).exif
{u'camera_model' : 'Spamcams 4242', 'exposure_time' : 0.3, ...}

Вариант с указанием типа:

class Poll(models.Model):
    ...
    votes = DictField(models.IntegerField())
>>> Poll(votes={'bob' : 3.14, 'alice' : '42'}, ...).save()
>>> Poll.objects.get(...).votes
{u'bob' : 3, u'alice' : 42}
Обновление данных
Post.objects.filter(...).update(title='Everything is the same')

Можно использовать для обновления оператор $set

.update(..., {'$set': {'title': 'Everything is the same'}})

А также функцию F()

Post.objects.filter(...).update(visits=F('visits')+1)

В результате получится что то такое:

.update(..., {'$inc': {'visits': 1}})
Использование низко-уровневых запросов

Если Вам не хватает возможностей Django ORM, можно использовать запросы к MongoDB минуя стандартный механизм.

raw_query() — принимает один аргумент, возвращает данные в виде стандартного Django queryset. Что хорошо для дальнейшей обработки данных.

Пример с geo данными, модель:

from djangotoolbox.fields import EmbeddedModelField
from django_mongodb_engine.contrib import MongoDBManager

class Point(models.Model):
    latitude = models.FloatField()
    longtitude = models.FloatField()

class Place(models.Model):
    ...
    location = EmbeddedModelField(Point)

    objects = MongoDBManager()

получим все точки рядом с конкретными координатами:

>>> here = {'latitude' : 42, 'longtitude' : 3.14}
>>> Place.objects.raw_query({'location' : {'$near' : here}})

raw_update() — используется если нам недостаточно стандартных средств для обновления данных.

Модель:

from django_mongodb_engine.contrib import MongoDBManager

class FancyNumbers(models.Model):
    foo = models.IntegerField()

    objects = MongoDBManager()

использование:

FancyNumbers.objects.raw_update({}, {'$bit' : {'foo' : {'or' : 42}}})

В данном примере выполняется побитовое or для каждого foo в базе.

На этом возможности данной связки не заканчиваются, но если пречислять все, то статья не оправдано затянется. Полное описание и примеры можно будет посмотреть по представленным ниже ссылкам.

Ссылки:
MongoDB
mongodb-engine
GitHub

Python и Twisted

twistedTwisted — это фреймворк на Python для разработки сетевых приложений, который среди многих других применений, может быть использован и для параллельной обработки данных — мультипроцессности. Это замечательно, но мне пришлось попотеть для того, чтобы найти то, что мне нужно.

twisted_ntwk_pro_ess_comp.indd

Я листал документацию Twisted и книгу O’Reilly Twisted. Существует также рецепт в Python Cookbook. Однако, самое интересное я нашел в статье Брюса Эккель — Параллельность с Python, Twisted и Flex. Также стоит прочитать первоначальные статьи Брюса Эккель про Twisted: Grokking Twisted.

Вот мои замечания о текущем примере Брюса.

Python CookbookЯ убрал Flex — отчасти потому, что мне это не нужно и я ничего не хочу знать об этом. В примере запускается контроллер, который инициализирует ряд отдельных параллельных процессов-вычислителей, в которых уже запускаются какие-то сложные действия (эти процессы называют solvers). Также тут имеется взаимодействие между контроллером и вычислителями. Хотя этот пример запускается только на одной машине, те принципы, о которых говориться в статье — не трудно распространить и на систему из нескольких компьютеров.

Для хорошего примера, как это работает, пожалуйста, смотрите оригинал статьи.

Вот solver.py который скопирован с оригинала. Настоящая «работа» происходит в методе step(). Я только добавил некоторую отладочную информацию для себя.

"""
solver.py
Original version by Bruce Eckel
Solves one portion of a problem, in a separate process on a separate CPU
"""
import sys, random, math
from twisted.spread import pb
from twisted.internet import reactor

class Solver(pb.Root):

    def __init__(self, id):
        print "solver.py %s: solver init" % id
        self.id = id

    def __str__(self): # String representation
        return "Solver %s" % self.id

    def remote_initialize(self, initArg):
        return "%s initialized" % self

    def step(self, arg):
        print "solver.py %s: solver step" % self.id
        "Simulate work and return result"
        result = 0
        for i in range(random.randint(1000000, 3000000)):
            angle = math.radians(random.randint(0, 45))
            result += math.tanh(angle)/math.cosh(angle)
        return "%s, %s, result: %.2f" % (self, str(arg), result)

    # Alias methods, for demonstration version:
    remote_step1 = step
    remote_step2 = step
    remote_step3 = step

    def remote_status(self):
        print "solver.py %s: remote_status" % self.id
        return "%s operational" % self

    def remote_terminate(self):
        print "solver.py %s: remote_terminate" % self.id
        reactor.callLater(0.5, reactor.stop)
        return "%s terminating..." % self

if __name__ == "__main__":
    port = int(sys.argv[1])
    reactor.listenTCP(port, pb.PBServerFactory(Solver(sys.argv[1])))
    reactor.run()

Вот controller.py. Он также скопирован из оригинальной статьи, но я убрал Flex и создал сигналы start и terminate в классе контроллера. Я не уверен, что это имеет смысл, но, по крайней мере, это позволило мне нормально использовать пример. Я также перенес метод terminate из FlexInterface в Controller.

"""
Controller.py
Original version by Bruce Eckel
Starts and manages solvers in separate processes for parallel processing.
"""
import sys
from subprocess import Popen
from twisted.spread import pb
from twisted.internet import reactor, defer

START_PORT = 5566
MAX_PROCESSES = 2

class Controller(object):

    def broadcastCommand(self, remoteMethodName, arguments, nextStep, failureMessage):
        print "controller.py: broadcasting..."
        deferreds = [solver.callRemote(remoteMethodName, arguments) 
                     for solver in self.solvers.values()]
        print "controller.py: broadcasted"
        reactor.callLater(3, self.checkStatus)

        defer.DeferredList(deferreds, consumeErrors=True).addCallbacks(
            nextStep, self.failed, errbackArgs=(failureMessage))

    def checkStatus(self):
        print "controller.py: checkStatus"
        for solver in self.solvers.values():
            solver.callRemote("status").addCallbacks(
                lambda r: sys.stdout.write(r + "\n"), self.failed, 
                errbackArgs=("Status Check Failed"))

    def failed(self, results, failureMessage="Call Failed"):
        print "controller.py: failed"
        for (success, returnValue), (address, port) in zip(results, self.solvers):
            if not success:
                raise Exception("address: %s port: %d %s" % (address, port, failureMessage))

    def __init__(self):
        print "controller.py: init"
        self.solvers = dict.fromkeys(
            [("localhost", i) for i in range(START_PORT, START_PORT+MAX_PROCESSES)])
        self.pids = [Popen(["python", "solver.py", str(port)]).pid
                     for ip, port in self.solvers]
        print "PIDS: ", self.pids
        self.connected = False
        reactor.callLater(1, self.connect)

    def connect(self):
        print "controller.py: connect"
        connections = []
        for address, port in self.solvers:
            factory = pb.PBClientFactory()
            reactor.connectTCP(address, port, factory)
            connections.append(factory.getRootObject())
        defer.DeferredList(connections, consumeErrors=True).addCallbacks(
            self.storeConnections, self.failed, errbackArgs=("Failed to Connect"))

        print "controller.py: starting parallel jobs"
        self.start()

    def storeConnections(self, results):
        print "controller.py: storeconnections"
        for (success, solver), (address, port) in zip(results, self.solvers):
            self.solvers[address, port] = solver
        print "controller.py: Connected; self.solvers:", self.solvers
        self.connected = True

    def start(self):
        "controller.py: Begin the solving process"
        if not self.connected:
            return reactor.callLater(0.5, self.start)
        self.broadcastCommand("step1", ("step 1"), self.step2, "Failed Step 1")

    def step2(self, results):
        print "controller.py: step 1 results:", results
        self.broadcastCommand("step2", ("step 2"), self.step3, "Failed Step 2")

    def step3(self, results):
        print "controller.py: step 2 results:", results
        self.broadcastCommand("step3", ("step 3"), self.collectResults, "Failed Step 3")

    def collectResults(self, results):
        print "controller.py: step 3 results:", results
        self.terminate()

    def terminate(self):
        print "controller.py: terminate"
        for solver in self.solvers.values():
            solver.callRemote("terminate").addErrback(self.failed, "Termination Failed")
        reactor.callLater(1, reactor.stop)
        return "Terminating remote solvers"

if __name__ == "__main__":
    controller = Controller()
    reactor.run()

Чтобы запустить программу, положите оба файла в одну папку и запустите

python controller.py

Вы должны увидеть, как загрузка двух процессоров (если их, конечно, у вас — 2) поднимется до 100%. А вот и вывод скрипта на экран:

controller.py: init
PIDS:  [12173, 12174]
solver.py 5567: solver init
solver.py 5566: solver init
controller.py: connect
controller.py: starting parallel jobs
controller.py: storeconnections
controller.py: Connected; self.solvers: {('localhost', 5567): , ('localhost', 5566): }
controller.py: broadcasting...
controller.py: broadcasted
solver.py 5566: solver step
solver.py 5567: solver step
controller.py: checkStatus
solver.py 5566: remote_status
Solver 5566 operational
solver.py 5567: remote_status
controller.py: step 1 results: [(True, 'Solver 5567, step 1, result: 683825.75'), (True, 'Solver 5566, step 1, result: 543177.17')]
controller.py: broadcasting...
controller.py: broadcasted
Solver 5567 operational
solver.py 5566: solver step
solver.py 5567: solver step
controller.py: checkStatus
solver.py 5566: remote_status
Solver 5566 operational
solver.py 5567: remote_status
controller.py: step 2 results: [(True, 'Solver 5567, step 2, result: 636793.90'), (True, 'Solver 5566, step 2, result: 335358.16')]
controller.py: broadcasting...
controller.py: broadcasted
Solver 5567 operational
solver.py 5566: solver step
solver.py 5567: solver step
controller.py: checkStatus
solver.py 5566: remote_status
Solver 5566 operational
solver.py 5567: remote_status
controller.py: step 3 results: [(True, 'Solver 5567, step 3, result: 847386.43'), (True, 'Solver 5566, step 3, result: 512120.15')]
controller.py: terminate
Solver 5567 operational
solver.py 5566: remote_terminate
solver.py 5567: remote_terminate

Интерфейсы к SOCKS5

anonymous-proxy-server

PHP

Используется cURL:

$ch=curl_init("http://www.fbi.gov");

   @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   // возвращаем результат, а не выводим его в печать
   @curl_setopt($ch, CURLOPT_VERBOSE, 0);
   //запрещаем выводить подробные сообщения о всех действиях
   @curl_setopt($ch, CURLOPT_HEADER, 0);
   // запрещаем Headers
   @curl_setopt($ch, CURLOPT_PROXY, "192.168.0.100:47047");
   //IP адрес и порт SOCKS5
   @curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); 
   //включаем использование SOCKS5
   $x=@curl_exec($ch);

curl_close($ch);

echo $x;
//выводим содержимое страницы в браузер

Python

Используем SocksiPy через wrapmodule()

1. Импортируем SocksiPy и другие модули
2. Запускаем setdefaultproxy() и передаем нужные данные
3. Загружаем целевой модуль внутри модуля wrapmodule()

# Импортируем нужные модули
import ftplib
import telnetlib
import urllib2

# Импортитуем SocksiPy
import socks

# Загружаем прокси
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, 'localhost', 9050)

# Перенаправляем FTP-сессию через SOCKS прокси
socks.wrapmodule(ftplib)
ftp = ftplib.FTP('cdimage.ubuntu.com')
ftp.login('anonymous', 'chicago@ic.fbi.gov')
print ftp.dir('cdimage')
ftp.close()

# Перенаправляем соединение telnet через SOCKS прокси
socks.wrapmodule(telnetlib)
tn = telnetlib.Telnet('achaea.com')
print tn.read_very_eager()
tn.close()

# Перенаправляем HTTP-запросы через SOCKS прокси
socks.wrapmodule(urllib2)
print urllib2.urlopen('http://www.whatismyip.com/automation/n09230945.asp').read()

 

Используем SocksiPy через socksocket

1. Импортируем SocksiPy
2. Загружаем socks.socksocket, который возвращает несколько параметров для socket.socket.
3. Запускаем setproxy() и передаем нужные данные
4. Подключаемся и используем сокет, как обычный…

import socks
s = socks.socksocket()
s.setproxy(socks.PROXY_TYPE_SOCKS5, 'localhost', 9050)
s.connect(('www.whatismyip.com', 80))
s.send('GET /automation/n09230945.asp HTTP/1.1\r\n\r\n')

data = ''
buf = s.recv(1024)
while len(buf):
data += buf
buf = s.recv(1024)
s.close()

print("Connected from %s." % (data))

Open Graph тэги вручную для тем WordPress

open-graph-facebookДля начала нужно заменить стандартный тег <html> на тот, что нам нужен. Открываете файл header.php и заменяете стандартный тег:

<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>

на этот код:

<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:fb="http://ogp.me/ns/fb#" 
      xmlns:og="http://ogp.me/ns#" <?php language_attributes(); ?>>

В этом же файле header.php перед закрывающим тегом </head> вставляете код тегов Open Graph Facebook:

<!--Open Graph Facebook-->

<?php if (have_posts()):while(have_posts()):the_post(); endwhile; endif;?>  

<!-- постоянные значения -->  

<meta property="fb:admins" content="ВАШ_ЛИЧНЫЙ_ID_FACEBOOK" />  

<!-- если это статья -->  

<?php if (is_single()) { ?>  
<meta property="og:url" content="<?php the_permalink(); ?> "/>  
<meta property="og:title" content="<?php single_post_title(''); ?>" />  
<meta property="og:description" 
         content="<?php echo strip_tags(get_the_excerpt($post->ID)); ?>" />  
<meta property="og:type" content="article" />  
<meta property="og:image" 
content="<?php if (function_exists('wp_get_attachment_thumb_url')) {echo 
wp_get_attachment_thumb_url(get_post_thumbnail_id($post->ID)); } ?>" />  

<!-- если это любая другая страница -->  

<?php } else { ?>  
<meta property="og:site_name" content="<?php bloginfo('name'); ?>" />  
<meta property="og:description" 
       content="<?php bloginfo('description'); ?>" />  
<meta property="og:type" content="website" />  
<meta property="og:image" content="http://ПУТЬ-К-КАРТИНКЕ/КАРТИНКА.jpg" /> 
<?php } ?>

Вставьте ссылку на картинку, которая будет отображаться по умолчанию, если в статье или на странице нет других картинок. Как правило, сюда вставляется ссылка на логотип.

Сохраняете изменения и проверяете работу тегов Open Graph, нажав на кнопку «Мне нравится» в любой статье блога.

Может быть ситуация, при которой картинка статьи всё равно публикуется некорректно или вся статья просто не отображается (как было у меня). Это значит, что функция wp_get_attachment_thumb_url() не работает. Тогда необходимо сделать следующие действия.

Замените этот тег:

<meta property="og:image" 
content="<?php if (function_exists('wp_get_attachment_thumb_url')) {echo 
wp_get_attachment_thumb_url(get_post_thumbnail_id($post->ID)); }?>" />

на этот тег:

<meta property="og:image" 
      content="<?php if (function_exists('catch_that_image')) 
      {echo catch_that_image(); }?>" />

Сохраните изменения. Затем справа в панели управления найдите ссылку на файл «Функции темы» (functions.php), откройте его и в конце кода перед знаком ?> вставьте следующий код:

function catch_that_image() {  
    global $post, $posts;  
    $first_img = '';  
    ob_start();  
    ob_end_clean();  
    $output = preg_match_all('/<img.+src=[\'"]([^\'"]+)[\'"] title="3 варианта, как настроить теги Open Graph Facebook для WordPress?" alt="3 варианта, как настроить теги Open Graph Facebook для WordPress?">/i', $post->post_content, $matches);  
    $first_img = $matches [1] [0];  
    if(empty($first_img)){  
            //Определяет картинку по умолчанию 
            $first_img = "http://ПУТЬ_К_КАРТИНКЕ_ПО_УМОЛЧАНИЮ/ФОТО.jpg";  
        }  
    return $first_img;  
}

Этот код описывает функцию catch_that_image(), которая находит первую картинку в статье блога и вставляет её ссылку в тег изображения Open Graph. Если в статье нет картинки, то эта функция использует изображение по умолчанию. Для этого вставьте ссылку нужного изображения в эту переменную:

$first_img = "http://ПУТЬ_К_КАРТИНКЕ_ПО_УМОЛЧАНИЮ/ФОТО.jpg";

Теперь сохраняйте изменения в файле functions.php, заходите на блог и проверяйте работу кнопок «Мне нравится». Всё должно работать корректно.